From 9dca89401e5cff764cfb69a74b9ec46e5d6d1156 Mon Sep 17 00:00:00 2001 From: Oneirical Date: Sat, 6 Apr 2024 12:54:12 -0400 Subject: [PATCH] Squashed commit of the following: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit bbab2cf0209279cef607f859097c35d21939b898 Merge: 61b07b7f599 ea40fa210b8 Author: Julien <96022417+Oneirical@users.noreply.github.com> Date: Fri Apr 5 20:23:58 2024 +0000 Merge branch 'master' into version commit 61b07b7f599417bee714a6e8924b64b7ae466694 Author: Oneirical Date: Fri Apr 5 16:22:49 2024 -0400 attempt to fix merge conflict commit bebb3c4edeb12ef0a27b087508501d8c0e6d7d0c Author: Oneirical Date: Fri Apr 5 16:14:32 2024 -0400 try making tidy happy again commit 6c727321f26ce61a52adc69cf965e0877de530d3 Author: Oneirical Date: Fri Apr 5 16:13:44 2024 -0400 sync ui_tests.rs with master commit 44bc9cae014808afad6b76cb7a02832b181adf76 Author: Oneirical Date: Fri Apr 5 16:10:19 2024 -0400 a worthy sacrifice to tidy (possible merge conflict) commit ea40fa210b87a322d2259852c149ffa212a3a0da Merge: 1921968cc54 6cf6fd38ec0 Author: bors Date: Fri Apr 5 17:28:45 2024 +0000 Auto merge of #123502 - bjorn3:sync_cg_clif-2024-04-05, r=bjorn3 Subtree sync for rustc_codegen_cranelift This fixes an ICE when compiling unchecked_shl/unchecked_shr. r? `@ghost` `@rustbot` label +A-codegen +A-cranelift +T-compiler commit 6cf6fd38ec06decc2e5896ca3659cd74b0b03363 Merge: 5958f5e08fa fbda869b4e2 Author: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri Apr 5 16:20:23 2024 +0000 Merge commit 'fbda869b4e230c788b6bce426038ba8419956f2d' into sync_cg_clif-2024-04-05 commit 1921968cc5403892739b43bdefe793a130badd15 Merge: 5958f5e08fa 9cb517aede5 Author: bors Date: Fri Apr 5 15:20:50 2024 +0000 Auto merge of #123497 - GuillaumeGomez:rollup-usqb4q9, r=GuillaumeGomez Rollup of 8 pull requests Successful merges: - #122334 (Vendor rustc_codegen_gcc) - #122894 (Move check for error in impl header outside of reporting) - #123149 (Port argument-non-c-like-enum to Rust) - #123311 (Match ergonomics: implement "`&`pat everywhere") - #123350 (Actually use the inferred `ClosureKind` from signature inference in coroutine-closures) - #123474 (Port `run-make/issue-7349` to a codegen test) - #123489 (handle rustc args properly in bootstrap) - #123496 (ping on wf changes, remove fixme) r? `@ghost` `@rustbot` modify labels: rollup commit fbda869b4e230c788b6bce426038ba8419956f2d Author: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed Mar 27 20:43:19 2024 +0000 Add a couple more sync impls to mini_core commit 454c87bdd442636b474d943c0e587750b46d822b Merge: 40d40fb2293 1bab6df32b7 Author: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri Apr 5 15:01:30 2024 +0000 Merge branch 'build_system_changes' commit 158c301cee972e36b2a615f98ce0ddd5db41a6f7 Author: Oneirical Date: Wed Apr 3 14:16:06 2024 -0400 rewrite version as an UI test try fixing merge conflict also fix root_entry_limit make tidy happy remove does-nothing fix: set back to 860 commit 9cb517aede5500e80d25a39ba9f079dcf29fb8d4 Merge: b0ca3cd9d42 6db7ac62332 Author: Guillaume Gomez Date: Fri Apr 5 16:38:52 2024 +0200 Rollup merge of #123496 - lcnr:wf-ping, r=compiler-errors ping on wf changes, remove fixme extend core type system pings to `wf.rs` r? `@compiler-errors` commit b0ca3cd9d42d797ccf8a2b172a237921d721dfed Merge: 0d5ee650f86 199589d8146 Author: Guillaume Gomez Date: Fri Apr 5 16:38:52 2024 +0200 Rollup merge of #123489 - onur-ozkan:handle-rustc-args-properly, r=clubby789 handle rustc args properly in bootstrap Because `RUSTFLAGS` gets overwritten during the conversion from `Cargo` to `Command`, the passed rustc args were being lost. This change combines the rustc args with the values that override `RUSTFLAGS`. Fixes #123228 commit 0d5ee650f8619021635e255447a036b2e98d42b8 Merge: 02ee8a8ceef 476156aedf4 Author: Guillaume Gomez Date: Fri Apr 5 16:38:51 2024 +0200 Rollup merge of #123474 - jieyouxu:issue-7349-port, r=Mark-Simulacrum Port `run-make/issue-7349` to a codegen test The test does not need to be a run-make test, it can use the codegen test infrastructure. Also took the opportunity to rename the test to `no-redundant-item-monomorphization` so it's not just some opaque issue number. Part of #121876. commit 02ee8a8ceeff811adfb065028a48c1947d97ef91 Merge: f2f8d8b722f 55e46612c1c Author: Guillaume Gomez Date: Fri Apr 5 16:38:51 2024 +0200 Rollup merge of #123350 - compiler-errors:async-closure-by-move, r=oli-obk Actually use the inferred `ClosureKind` from signature inference in coroutine-closures A follow-up to https://github.com/rust-lang/rust/pull/123349, which fixes another subtle bug: We were not taking into account the async closure kind we infer during closure signature inference. When I pass a closure directly to an arg like `fn(x: impl async FnOnce())`, that should have the side-effect of artificially restricting the kind of the async closure to `ClosureKind::FnOnce`. We weren't doing this -- that's a quick fix; however, it uncovers a second, more subtle bug with the way that `move`, async closures, and `FnOnce` interact. Specifically, when we have an async closure like: ``` let x = Struct; let c = infer_as_fnonce(async move || { println!("{x:?}"); } ``` The outer closure captures `x` by move, but the inner coroutine still immutably borrows `x` from the outer closure. Since we've forced the closure to by `async FnOnce()`, we can't actually *do* a self borrow, since the signature of `AsyncFnOnce::call_once` doesn't have a borrowed lifetime. This means that all `async move` closures that are constrained to `FnOnce` will fail borrowck. We can fix that by detecting this case specifically, and making the *inner* async closure `move` as well. This is always beneficial to closure analysis, since if we have an `async FnOnce()` that's `move`, there's no reason to ever borrow anything, so `move` isn't artificially restrictive. commit f2f8d8b722fdacc6a6e02c2289e3e36571749a68 Merge: c36c0095776 e5376f3947b Author: Guillaume Gomez Date: Fri Apr 5 16:38:50 2024 +0200 Rollup merge of #123311 - Jules-Bertholet:andpat-everywhere, r=Nadrieril Match ergonomics: implement "`&`pat everywhere" Implements the eat-two-layers (feature gate `and_pat_everywhere`, all editions) ~and the eat-one-layer (feature gate `and_eat_one_layer_2024`, edition 2024 only, takes priority on that edition when both feature gates are active)~ (EDIT: will be done in later PR) semantics. cc #123076 r? ``@Nadrieril`` ``@rustbot`` label A-patterns A-edition-2024 commit c36c0095776f1cbc39d15a206b4b1018b329b4aa Merge: cb6a1c8d455 fad8213150b Author: Guillaume Gomez Date: Fri Apr 5 16:38:50 2024 +0200 Rollup merge of #123149 - jieyouxu:rmake-arguments-non-c-like-enum, r=Mark-Simulacrum Port argument-non-c-like-enum to Rust Part of #121876. commit cb6a1c8d455a26c82165e3285557da2d82e22385 Merge: 8873ca57f86 5333f2a9d15 Author: Guillaume Gomez Date: Fri Apr 5 16:38:49 2024 +0200 Rollup merge of #122894 - compiler-errors:downgrade, r=lcnr Move check for error in impl header outside of reporting Fixes #121006 r? lcnr test location kinda sucks, can move it if needed commit 8873ca57f8646328bf0a10ee94918c8c597706e9 Merge: 4563f70c3b5 0f5140e3834 Author: Guillaume Gomez Date: Fri Apr 5 16:38:49 2024 +0200 Rollup merge of #122334 - GuillaumeGomez:vendor-cg_gcc, r=Mark-Simulacrum Vendor rustc_codegen_gcc I used https://github.com/rust-lang/rust/pull/115274 as base for this update. r? `@bjorn3` commit 1bab6df32b7e593f75ab6470c0b650b345107995 Author: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Tue Apr 2 14:17:35 2024 +0000 Remove all checks for CI in the build system And introduce the CG_CLIF_EXPENSIVE_CHECKS env var in the place. commit f269cdd80582ec92972483641676e4a933b583a0 Author: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri Apr 5 13:56:21 2024 +0000 Move disabling incr comp and denying warnings to the CI config commit 603b2800f7fa61947b35419f1a5a33e265792001 Author: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Tue Apr 2 15:17:56 2024 +0000 Revoke all permissions from GHA workflows where possible commit 65342df8cd9ed8ca5311edc69f445fdb7b60a191 Author: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Tue Apr 2 15:11:20 2024 +0000 Dedup default shell specification for GHA commit 91ffd74de41fa473bf8e7ac94b8159578ba3e94c Author: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Tue Apr 2 15:05:06 2024 +0000 Simplify GHA CI workflow commit 5958f5e08fa88ee95ede8c00f1b89befe0372d54 Merge: 4563f70c3b5 3ad9c83cd25 Author: bors Date: Fri Apr 5 13:17:09 2024 +0000 Auto merge of #123317 - RalfJung:test-in-miri, r=m-ou-se,saethlin,onur-ozkan Support running library tests in Miri This adds a new bootstrap subcommand `./x.py miri` which can test libraries in Miri. This is in preparation for eventually doing that as part of bors CI, but this PR only adds the infrastructure, and doesn't enable it yet. `@rust-lang/bootstrap` should this be `x.py test --miri library/core` or `x.py miri library/core`? The flag has the advantage that we don't have to copy all the arguments from `Subcommand::Test`. It has the disadvantage that most test steps just ignore `--miri` and still run tests the regular way. For clippy you went the route of making it a separate subcommand. ~~I went with a flag now as that seemed easier, but I can change this.~~ I made it a new subcommand. Note however that the regular cargo invocation would be `cargo miri test ...`, so `x.py` is still going to be different in that the `test` is omitted. That said, we could also make it `./x.py miri-test` to make that difference smaller -- that's in fact more consistent with the internal name of the command when bootstrap invokes cargo. `@rust-lang/libs` ~~unfortunately this PR does some unholy things to the `lib.rs` files of our library crates.~~ `@m-ou-se` found a way that entirely avoids library-level hacks, except for some new small `lib.miri.rs` files that hopefully you will never have to touch. There's a new hack in cargo-miri but there it is in good company... commit 6db7ac6233244c550b3b0a030387419ab2ac9ddd Author: lcnr Date: Fri Apr 5 15:09:48 2024 +0200 ping on wf changes, remove fixme commit 40d40fb2293734b6ac9c77e764bf0183819e4a95 Author: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Tue Apr 2 14:29:00 2024 +0000 Fix warning in mini_core commit 737421a25b9a46aca017ffef1b1d648e278d1d87 Author: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri Apr 5 12:15:41 2024 +0000 Rustup to rustc 1.79.0-nightly (385fa9d84 2024-04-04) commit 5a9940fe3dd8e48d98b92d13a03372ff7da93f31 Merge: 64d6da5cda2 6728f2fef42 Author: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri Apr 5 11:39:51 2024 +0000 Sync from rust 385fa9d845dd326c6bbfd58c22244215e431948a commit 4563f70c3b599411836e285591479f4a3d364708 Merge: d009f60b55f aa9c9a36c06 Author: bors Date: Fri Apr 5 11:11:17 2024 +0000 Auto merge of #122070 - Zoxc:dep-edges-from-previous, r=cjgillot Encode dep graph edges directly from the previous graph when promoting This encodes dep graph edges directly from the previous graph when promoting nodes from a previous session, avoiding allocations / copies. ~~Based on https://github.com/rust-lang/rust/pull/122064 and https://github.com/rust-lang/rust/pull/116375.~~
BenchmarkBeforeAfter
TimeTime%
🟣 clap:check:unchanged0.4177s0.4072s💚 -2.52%
🟣 hyper:check:unchanged0.1430s0.1420s -0.69%
🟣 regex:check:unchanged0.3106s0.3038s💚 -2.19%
🟣 syn:check:unchanged0.5823s0.5688s💚 -2.33%
🟣 syntex_syntax:check:unchanged1.3992s1.3692s💚 -2.14%
Total2.8528s2.7910s💚 -2.17%
Summary1.0000s0.9803s💚 -1.97%
commit 3ad9c83cd2538f865b62c87f998cb25e599da029 Author: Ralf Jung Date: Fri Apr 5 08:51:46 2024 +0200 miri: go look for the item in all crates of the right name commit d009f60b55fe4527e7ddf122bc4520f351d7b9d4 Merge: c0ddaef0757 af81ab76288 Author: bors Date: Fri Apr 5 09:09:35 2024 +0000 Auto merge of #123469 - belovdv:remove-miri-jobserver-fixme, r=petrochenkov remove miri jobserver workaround This PR removes workaround, added in #113730, since jobserver is kept after [rust-lang/cargo#12776](https://github.com/rust-lang/cargo/pull/12776) commit 199589d81466a4b436bd41cc0dfd2f35c908a951 Author: onur-ozkan Date: Fri Apr 5 10:52:36 2024 +0300 handle rustc args properly in bootstrap Because `RUSTFLAGS` gets overwritten during the conversion from `Cargo` to `Command`, the passed rustc args were being lost. This change combines the rustc args with the values that override `RUSTFLAGS`. Signed-off-by: onur-ozkan commit c0ddaef075748674140263840a185082f44458e9 Merge: 3d7e88148a0 b0b7c860e13 Author: bors Date: Fri Apr 5 04:34:05 2024 +0000 Auto merge of #123444 - saethlin:const-eval-inline-cycles, r=tmiasko Teach MIR inliner query cycle avoidance about const_eval_select Fixes https://github.com/rust-lang/rust/issues/122659 r? tmiasko commit 3d7e88148a00c99e444c8f287d73ebe1846bc7aa Merge: 9cbaa015991 e8b0c305780 Author: bors Date: Fri Apr 5 02:32:03 2024 +0000 Auto merge of #123484 - jhpratt:rollup-usz4e64, r=jhpratt Rollup of 9 pull requests Successful merges: - #123206 (Require Pointee::Metadata to be Freeze) - #123363 (change `NormalizesTo` to fully structurally normalize) - #123407 (Default to light theme if JS is enabled but not working) - #123417 (Add Description for cargo in rustdoc documentation) - #123437 (Manually run `clang-format` on `CoverageMappingWrapper.cpp`) - #123454 (hir: Use `ItemLocalId::ZERO` in a couple more places) - #123464 (Cleanup: Rename `HAS_PROJECTIONS` to `HAS_ALIASES` etc.) - #123477 (do not ICE in `fn forced_ambiguity` if we get an error) - #123478 (CFI: Add test for `call_once` addr taken) r? `@ghost` `@rustbot` modify labels: rollup commit e8b0c3057807e50ac1f95fdba36d79b72fbd206d Merge: e01d3e0824c b53a0f2c9e9 Author: Jacob Pratt Date: Thu Apr 4 21:16:59 2024 -0400 Rollup merge of #123478 - maurer:cfi-call-once-addr-taken, r=compiler-errors CFI: Add test for `call_once` addr taken One of the proposed ways to reduce the non-passed argument erasure would cause this test to fail. Adding this now ensures that any attempt to reduce non-passed argument erasure won't make the same mistake. r? `@compiler-errors` cc `@rcvalle` commit e01d3e0824cd24dc02c0b22db2fa0d4677f1a0c3 Merge: 58eb6e58031 9444ca354a5 Author: Jacob Pratt Date: Thu Apr 4 21:16:58 2024 -0400 Rollup merge of #123477 - lcnr:forced_ambig-no-ice, r=compiler-errors do not ICE in `fn forced_ambiguity` if we get an error see the comment. currently causing an ICE in typenum which we've been unable to minimize. r? `@compiler-errors` commit 58eb6e580316b01cfbd68d9d02c98e1e68daf249 Merge: 929e0db6a9a 6f17b7f0aba Author: Jacob Pratt Date: Thu Apr 4 21:16:58 2024 -0400 Rollup merge of #123464 - fmease:rn-has-proj-to-has-aliases, r=compiler-errors Cleanup: Rename `HAS_PROJECTIONS` to `HAS_ALIASES` etc. The name of the bitflag `HAS_PROJECTIONS` and of its corresponding method `has_projections` is quite historical dating back to a time when projections were the only kind of alias type. I think it's time to update it to clear up any potential confusion for newcomers and to reduce unnecessary friction during contributor onboarding. r? types commit 929e0db6a9a31b6079839df5834ee211d191802f Merge: daef0fd8782 17475de5de1 Author: Jacob Pratt Date: Thu Apr 4 21:16:57 2024 -0400 Rollup merge of #123454 - petrochenkov:zeroindex2, r=fmease hir: Use `ItemLocalId::ZERO` in a couple more places Follow up to https://github.com/rust-lang/rust/pull/123415 and https://github.com/rust-lang/rust/pull/123419. commit daef0fd87829dc063201828f591c70ac47beffd9 Merge: 50603cbb6d0 f6b97ef5e56 Author: Jacob Pratt Date: Thu Apr 4 21:16:57 2024 -0400 Rollup merge of #123437 - Zalathar:clang-format, r=cuviper Manually run `clang-format` on `CoverageMappingWrapper.cpp` In the current version of #123409, there are several unrelated changes to `CoverageMappingWrapper.cpp` that seem to be the result of running `clang-format` on that file. Instead of asking for those changes to be undone, I figure it's easier to just make them myself as a separate PR, since I was vaguely intending to do that at some point anyway. In a few cases I've strategically added comments to make the grouping of parameters a little nicer, but mostly it doesn't matter much. commit 50603cbb6d019789e5838a0ff720102c38a5ee58 Merge: ac298726afb 612acf8397f Author: Jacob Pratt Date: Thu Apr 4 21:16:56 2024 -0400 Rollup merge of #123417 - harryhanYuhao:master, r=GuillaumeGomez Add Description for cargo in rustdoc documentation As most people use cargo now, I prioritised the description for cargo in rustdoc documentation. I also added how to open the generated doc with cargo. Btw, may I ask how to use `./x tidy`? It says `warning: `tidy` is not installed;` commit ac298726afbb2719c966153539b4229da6c8b8b6 Merge: fcb0e9d07a3 a815b97850e Author: Jacob Pratt Date: Thu Apr 4 21:16:56 2024 -0400 Rollup merge of #123407 - GuillaumeGomez:js-failed-theme, r=notriddle Default to light theme if JS is enabled but not working It doesn't [fix] #123399 but it allows to reduce the problem: * if JS is completely disabled, then `noscript.css` will be applied * if JS failed for any reason, then the light theme will be applied (because `noscript.css` won't be applied) r? `@notriddle` commit fcb0e9d07a3397731acd20059b8adf47b6863cd6 Merge: de2cb0d76c7 92b280ce81e Author: Jacob Pratt Date: Thu Apr 4 21:16:56 2024 -0400 Rollup merge of #123363 - lcnr:normalizes-to-zero-to-inf, r=BoxyUwU change `NormalizesTo` to fully structurally normalize notes in https://hackmd.io/wZ016dE4QKGIhrOnHLlThQ need to also update the dev-guide once this PR lands. in short, the setup is now as follows: `normalizes-to` internally implements one step normalization, applying that normalization to the `goal.predicate.term` causes the projected term to get recursively normalized. With this `normalizes-to` normalizes until the projected term is rigid, meaning that we normalize as many steps necessary, but at least 1. To handle rigid aliases, we add another candidate only if the 1 to inf step normalization failed. With this `normalizes-to` is now full structural normalization. We can now change `AliasRelate` to simply emit `normalizes-to` goals for the rhs and lhs. This avoids the concerns from https://github.com/rust-lang/trait-system-refactor-initiative/issues/103 and generally feels cleaner commit de2cb0d76c79720eb641e3ebbacf26907eac69dc Merge: 385fa9d845d 8f5a28e0aa3 Author: Jacob Pratt Date: Thu Apr 4 21:16:55 2024 -0400 Rollup merge of #123206 - stepancheg:pointee-metadata-freeze, r=Amanieu Require Pointee::Metadata to be Freeze So pointee metadata can be used in anonymous statics. This is prerequisite for implementing ThinBox without allocation for ZST. See https://github.com/rust-lang/rust/pull/123184#discussion_r1544627488 r? joboet commit 9cbaa015991b7ac5f6e1b4d713ab9096573ad30d Merge: 385fa9d845d 5d66521dfef Author: bors Date: Fri Apr 5 00:30:47 2024 +0000 Auto merge of #123465 - flip1995:clippy-subtree-update, r=Manishearth Clippy subtree update r? `@Manishearth` commit 55e46612c1ccceb30a7a6acf11fd485f34e393e5 Author: Michael Goulet Date: Wed Apr 3 12:16:17 2024 -0400 Force `move` async-closures that are `FnOnce` to make their inner coroutines also `move` commit 3d9d5d7c96ae3df2cfc47e933ab11ad5fa30f3bc Author: Michael Goulet Date: Mon Apr 1 14:47:12 2024 -0400 Actually use the inferred ClosureKind from signature inference in coroutine-closures commit 5d66521dfefe64c0e4f571edcd4408a07505ff48 Author: y21 <30553356+y21@users.noreply.github.com> Date: Fri Apr 5 00:17:27 2024 +0200 use `Lrc` instead of the aliased type `Arc` directly commit b53a0f2c9e96778a044cb72472aa898f9804471d Author: Matthew Maurer Date: Thu Apr 4 22:06:58 2024 +0000 CFI: Add test for `call_once` addr taken One of the proposed ways to reduce the non-passed argument erasure would cause this test to fail. Adding this now ensures that any attempt to reduce non-passed argument erasure won't make the same mistake. commit 9444ca354a5ed82ab7c8b264117c9a9436948ce9 Author: lcnr Date: Thu Apr 4 07:47:13 2024 +0200 do not ICE in forced ambiguity if we get an error commit a815b97850e487f5c668edf83357cf871b3db57a Author: Guillaume Gomez Date: Thu Apr 4 23:49:34 2024 +0200 Add regression test to ensure that even if JS is enabled but not working, a theme will still get applied commit f2ff9c903548d88211df90cd4e1673577ff58ad5 Author: Guillaume Gomez Date: Thu Apr 4 23:48:35 2024 +0200 Update browser-ui-test version to 0.17.1 commit 476156aedf4b4bc74e10d82d59c3a7c4429a8d0e Author: 许杰友 Jieyou Xu (Joe) Date: Thu Apr 4 21:59:08 2024 +0100 Port issue-7349 to a codegen test commit 385fa9d845dd326c6bbfd58c22244215e431948a Merge: a4b11c8e605 0dca1368411 Author: bors Date: Thu Apr 4 20:52:52 2024 +0000 Auto merge of #123097 - oli-obk:perf_experiment, r=petrochenkov Try using a `dyn Debug` trait object instead of a closure These closures were introduced in https://github.com/rust-lang/rust/pull/93098 let's see if we can't use fmt::Arguments instead cc `@Aaron1011` commit af81ab762888eb04d01e9ad5269df5202d6a38b8 Author: belovdv <70999565+belovdv@users.noreply.github.com> Date: Thu Apr 4 22:40:00 2024 +0300 remove miri jobserver workaround commit a809a726671bc81206e2ab9cba59873f6e741212 Author: Philipp Krones Date: Thu Apr 4 19:53:00 2024 +0200 Update Cargo.lock commit eedf15a1dc54097439ecd524e315665f18eaf31c Merge: 96eaf553e54 9725c4a1625 Author: Philipp Krones Date: Thu Apr 4 19:52:55 2024 +0200 Merge commit '9725c4a162502a02c1c67fdca6b797fe09b2b73c' into clippy-subtree-update commit 9725c4a162502a02c1c67fdca6b797fe09b2b73c Merge: 5a9e9b0e65d bb023e90d8d Author: bors Date: Thu Apr 4 17:50:22 2024 +0000 Auto merge of #12632 - flip1995:rustup, r=flip1995 Rustup r? `@ghost` changelog: none commit bb023e90d8dfbdee714aa33cfa44b91753a99a03 Author: Philipp Krones Date: Thu Apr 4 19:48:53 2024 +0200 Bump nightly version -> 2024-04-04 commit 277303b210a3501211961d69ad2a09cae0b2643f Merge: 53e31dc45ce 5a9e9b0e65d Author: Philipp Krones Date: Thu Apr 4 19:26:44 2024 +0200 Merge remote-tracking branch 'upstream/master' into rustup commit a4b11c8e6057bd47f8d241baafd5ae1e813d62fd Merge: 0fd571286ef 4e8d2f0040f Author: bors Date: Thu Apr 4 17:42:07 2024 +0000 Auto merge of #121394 - oli-obk:define_opaque_types, r=compiler-errors some smaller DefiningOpaqueTypes::No -> Yes switches r? `@compiler-errors` These are some easy cases, so let's get them out of the way first. I added tests exercising the specialization code paths that I believe weren't tested so far. follow-up to https://github.com/rust-lang/rust/pull/117348 commit 6f17b7f0aba4e07f5aa202ecb1e95e0e3d97f828 Author: León Orell Valerian Liehr Date: Thu Apr 4 19:26:17 2024 +0200 Rename HAS_PROJECTIONS to HAS_ALIASES etc. commit 4e8d2f0040f108e3f07854b231a5987005217763 Author: Oli Scherer Date: Wed Feb 21 15:50:40 2024 +0000 Add regression test commit ede0556ab538ccaa928299812519db95aff9d7ca Author: Oli Scherer Date: Wed Feb 21 15:36:49 2024 +0000 Effects are boolean consts and don't contain opaque types commit 0183d92df0591b25363b3e12ae0a4a8f1b0b076c Author: Oli Scherer Date: Wed Feb 21 15:33:09 2024 +0000 Allow defining opaque types when checking const equality bounds commit 0fd571286ef6df8a6cce06d432013239a8c0665f Merge: 96eaf553e54 83bd12c70fd Author: bors Date: Thu Apr 4 15:39:00 2024 +0000 Auto merge of #123377 - oli-obk:private_projection, r=compiler-errors Only inspect user-written predicates for privacy concerns fixes #123288 Previously we looked at the elaborated predicates, which, due to adding various bounds on fields, end up requiring trivially true bounds. But these bounds can contain private types, which the privacy visitor then found and errored about. commit 29fba9f994d5c32448cb6937aaae9b319ddcf392 Author: Oli Scherer Date: Wed Feb 21 15:29:44 2024 +0000 Add regression test commit 8e226e092eba650ce0e71d1336519e4045a8ba0e Author: Oli Scherer Date: Wed Feb 21 14:59:43 2024 +0000 Add some regression tests for opaque types and const generics commit ba316a902d4a350f41bfaeba17df66b582de9d99 Author: Oli Scherer Date: Thu Apr 4 14:53:31 2024 +0000 amend to Switch `can_eq` and `can_sub` to `DefineOpaqueTypes::Yes` commit 83bd12c70fd34dece71bcc632ee3df64036ca1d8 Author: Oli Scherer Date: Tue Apr 2 17:27:35 2024 +0000 Only inspect user-written predicates for privacy concerns commit 169a045dca0e8cc7c720a962cef274fb5f8fafef Author: Oli Scherer Date: Wed Feb 21 14:50:17 2024 +0000 Switch upcast projections to allowing opaque types and add a test showing it works. The old solver was already ICEing on this test before this change commit cdcca7e8f4842fe4a3b64c0e5ac7d25025950889 Author: Oli Scherer Date: Wed Feb 21 14:29:28 2024 +0000 Switch `can_eq` and `can_sub` to `DefineOpaqueTypes::Yes` They are mostly used in diagnostics anyway commit 612acf8397f0f8d55fc533d61ea835b73a1e696d Author: Harry Han Date: Thu Apr 4 15:04:46 2024 +0100 rustdoc prioritise cargo doc: suggestions applied commit 96eaf553e547e36003e235a9de26c11460712231 Merge: ca7d34efa94 4ba3f46be3c Author: bors Date: Thu Apr 4 13:10:22 2024 +0000 Auto merge of #123455 - matthiaskrgr:rollup-b6nu296, r=matthiaskrgr Rollup of 9 pull requests Successful merges: - #121546 (Error out of layout calculation if a non-last struct field is unsized) - #122448 (Port hir-tree run-make test to ui test) - #123212 (CFI: Change type transformation to use TypeFolder) - #123218 (Add test for getting parent HIR for synthetic HIR node) - #123324 (match lowering: make false edges more precise) - #123389 (Avoid panicking unnecessarily on startup) - #123397 (Fix diagnostic for qualifier in extern block) - #123431 (Stabilize `proc_macro_byte_character` and `proc_macro_c_str_literals`) - #123439 (coverage: Remove useless constants) r? `@ghost` `@rustbot` modify labels: rollup commit 4ba3f46be3c28ba5b17c9a7b732b569868ad7d01 Merge: ad300b67386 e08fdb0f2fc Author: Matthias Krüger Date: Thu Apr 4 14:51:18 2024 +0200 Rollup merge of #123439 - Zalathar:constants, r=oli-obk coverage: Remove useless constants After #122972 and #123419, these constants don't serve any useful purpose, so get rid of them. `@rustbot` label +A-code-coverage commit ad300b673865d25b76e4d00da58c80df0bcba271 Merge: f254ab08f15 fbc56dfac13 Author: Matthias Krüger Date: Thu Apr 4 14:51:18 2024 +0200 Rollup merge of #123431 - slanterns:literal_byte_character_c_string_stabilize, r=dtolnay Stabilize `proc_macro_byte_character` and `proc_macro_c_str_literals` This PR stabilizes `proc_macro_byte_character` and `proc_macro_c_str_literals`: ```rust // proc_macro::Literal impl Literal { pub fn byte_character(byte: u8) -> Literal; pub fn c_string(string: &CStr) -> Literal } ```
Tracking issue: https://github.com/rust-lang/rust/issues/115268, https://github.com/rust-lang/rust/issues/119750. Implementation PR: https://github.com/rust-lang/rust/pull/112711, https://github.com/rust-lang/rust/pull/119651. FCPs already completed in their respective tracking issues. Closes https://github.com/rust-lang/rust/issues/115268. Closes https://github.com/rust-lang/rust/issues/119750. r? libs-api commit f254ab08f155ae118a71b438fa56d85e9251b0ce Merge: ee5009e745b 109daa2d4ba Author: Matthias Krüger Date: Thu Apr 4 14:51:17 2024 +0200 Rollup merge of #123397 - krtab:foreign_fn_qualif_diag, r=petrochenkov Fix diagnostic for qualifier in extern block Closes: https://github.com/rust-lang/rust/issues/123306 commit ee5009e745b4088f8ef942ab9bcf6f28b7bbac0d Merge: 504a78e2f24 7b8f93ef4c2 Author: Matthias Krüger Date: Thu Apr 4 14:51:17 2024 +0200 Rollup merge of #123389 - ChrisDenton:dont-panic-on-startup, r=joboet Avoid panicking unnecessarily on startup On Windows, in `lang_start` we add an exception handler to catch stack overflows and we also reserve some stack space for the handler. Both of these are useful but they're not strictly necessary. The standard library has to work without them (e.g. if Rust is used from a foreign entry point) and the negative effect of not doing them is limited (i.e. you don't get the friendly stack overflow message). As we really don't want to panic pre-main unless absolutely necessary, it now won't panic on failure. I've added some debug assertions so as to avoid programmer error. commit 504a78e2f24a7ce0e11116429c86c0be9553c79c Merge: 7c2d4eaf922 e2ebaa1a08a Author: Matthias Krüger Date: Thu Apr 4 14:51:16 2024 +0200 Rollup merge of #123324 - Nadrieril:false-edges2, r=matthewjasper match lowering: make false edges more precise When lowering match expressions, we add false edges to hide details of the lowering from borrowck. Morally we pretend we're testing the patterns (and guards) one after the other in order. See the tests for examples. Problem is, the way we implement this today is too coarse for deref patterns. In deref patterns, a pattern like `deref [1, x]` matches on a `Vec` by creating a temporary to store the output of the call to `deref()` and then uses that to continue matching. Here the pattern has a binding, which we set up after the pre-binding block. Problem is, currently the false edges tell borrowck that the pre-binding block can be reached from a previous arm as well, so the `deref()` temporary may not be initialized. This triggers an error when we try to use the binding `x`. We could call `deref()` a second time, but this opens the door to soundness issues if the deref impl is weird. Instead in this PR I rework false edges a little bit. What we need from false edges is a (fake) path from each candidate to the next, specifically from candidate C's pre-binding block to next candidate D's pre-binding block. Today, we link the pre-binding blocks directly. In this PR, I link them indirectly by choosing an earlier node on D's success path. Specifically, I choose the earliest block on D's success path that doesn't make a loop (if I chose e.g. the start block of the whole match (which is on the success path of all candidates), that would make a loop). This turns out to be rather straightforward to implement. r? `@matthewjasper` if you have the bandwidth, otherwise let me know commit 7c2d4eaf922664882d3af1041f776019d02f9562 Merge: f03535b2971 f0296029206 Author: Matthias Krüger Date: Thu Apr 4 14:51:16 2024 +0200 Rollup merge of #123218 - compiler-errors:synthetic-hir-parent, r=petrochenkov Add test for getting parent HIR for synthetic HIR node Fixes #122991, which was actually fixed by #123415 commit f03535b29712441999544e3306582fb41b5c0f4f Merge: 0b54db7e3fe bc8c3eff6b8 Author: Matthias Krüger Date: Thu Apr 4 14:51:15 2024 +0200 Rollup merge of #123212 - rcvalle:rust-cfi-use-type-folder, r=compiler-errors CFI: Change type transformation to use TypeFolder Change type transformation to use TypeFolder. cc `@compiler-errors` `@maurer` commit 0b54db7e3feffad43cc55c17ee26104feb24739f Merge: d5a657c95ca 2575b8e79c8 Author: Matthias Krüger Date: Thu Apr 4 14:51:15 2024 +0200 Rollup merge of #122448 - high-cloud:move-hir-tree, r=oli-obk Port hir-tree run-make test to ui test As part of #121876 cc `@jieyouxu` commit d5a657c95ca2f29c356c278b4e0be43bd8a7743d Merge: 4c6c6298664 313714331ac Author: Matthias Krüger Date: Thu Apr 4 14:51:14 2024 +0200 Rollup merge of #121546 - gurry:121473-ice-sizeof-mir-op, r=oli-obk Error out of layout calculation if a non-last struct field is unsized Fixes #121473 Fixes #123152 commit 17475de5de1978afbea982ba4efeaa6cea259d4a Author: Vadim Petrochenkov Date: Thu Apr 4 14:43:49 2024 +0300 hir: Use `ItemLocalId` in a couple more places commit 5a9e9b0e65d27bb294a5e13ca554878b43af6224 Merge: 398a52a8dc6 b6486fae5c9 Author: bors Date: Thu Apr 4 11:39:45 2024 +0000 Auto merge of #12628 - franciscoBSalgueiro:fix-book-links, r=flip1995 Add missing links in the book Added a few missing links marked with FIXME in the development book. changelog: none commit 7b8f93ef4c2b01b6cc7656123301f0f5e322b603 Author: Chris Denton Date: Thu Apr 4 10:48:11 2024 +0000 Add comments about using debug_assert commit ca7d34efa94afe271accf2bd3d44152a5bd6fff1 Merge: 4c6c6298664 8289dadfbc5 Author: bors Date: Thu Apr 4 10:45:21 2024 +0000 Auto merge of #121026 - Zalathar:version, r=oli-obk coverage: Correctly report and check LLVM's coverage mapping version I was puzzled by the fact that the LLVM 18 update (#120055) didn't need to modify this version check, despite the fact that LLVM 18 uses a newer version of the coverage mapping format. This turned out to be because we were inappropriately hard-coding a specific version (`Version6`) in the C++ wrapper, instead of using `CovMapVersion::CurrentVersion` to reflect the version actually used by LLVM on our behalf. This PR fixes that, and also changes the Rust-side version check to accept the new coverage mapping version used by LLVM 18, since the necessary compatibility work has already been done. --- ### Quick history of `LLVMRustCoverageMappingVersion`: - Originally it returned LLVM's `coverage::CovMapVersion::CurrentVersion`, as intended. The Rust-side code would verify it, and also embed it as the actual coverage version number in the output binary. - At some point it was changed to a hard-coded value, to work around a (now-irrelevant) compatibility issue. This was incorrect (but mostly benign), because the override should have been performed on the Rust side instead, after verifying LLVM's value. - Later contributors dutifully updated the hard-coded value, because they didn't have enough context to identify the problem. - With this PR, it once again returns LLVM's current coverage version number, and the Rust-side code checks it against an expected range. We don't override the result, but we do indicate where that override should occur if it ever becomes necessary. commit 2575b8e79c881db316af12ad66191a4b1000ff54 Author: Yaodong Yang Date: Thu Mar 14 01:23:43 2024 +0800 move hir-tree test from run-make to ui test commit 92b280ce81e2cf9834ed1965ae4e63e80bce0dc1 Author: lcnr Date: Tue Apr 2 15:12:20 2024 +0200 normalizes-to change from '1' to '0 to inf' steps commit 313714331ac3fbc63e767fe95d175f293cf5d875 Author: Gurinder Singh Date: Thu Apr 4 15:50:36 2024 +0530 Error out of layout calculation if a non-last struct field is unsized Fixes an ICE that occurs when a struct with an unsized field at a non-last position is const evaluated. commit 10e8bca7fe00b4b10eca33a8d70ebb06d71cd621 Author: Oli Scherer Date: Wed Feb 21 12:31:41 2024 +0000 FRU remaining fields does not actually define opaque types commit 95948b75b2e036f07462c3f7a51d7ced659057a3 Author: Oli Scherer Date: Wed Feb 21 11:44:59 2024 +0000 Use `DefineOpaqueTypes::Yes` since we are guaranteed to error already commit 2247aaf276029888fc7c14a372d9033276ec0c86 Author: Oli Scherer Date: Wed Feb 21 11:42:39 2024 +0000 Use `DefineOpaqueTypes::Yes` where the new solver is unconditionally used already commit 82ceed2add79244d53a960735a68ce85d55a4232 Author: Oli Scherer Date: Wed Feb 21 11:35:50 2024 +0000 Specialization can switch to `DefineOpaqueTypes::Yes` without having an effect. The reason is that in specialization graph computation we use `DefiningAnchor::Error`, so there's no difference anyway. And in the other use cases, we * already errored in the specialization_graph computation, or * already errored in coherence, or * are comparing opaque types with inference variables already, or * there are no opaque types involved commit b8bd9815455ac3c1bff865dee30aa694ad19beb6 Author: Oli Scherer Date: Wed Feb 21 11:30:57 2024 +0000 Specialization already rejects defining opaque types commit 150448d2e043061cfa39f94b0d87e1c80fb6a121 Author: Oli Scherer Date: Wed Feb 21 10:43:50 2024 +0000 use `DefineOpaqueTypes::Yes` in rustdoc Since we have a `DefiningAnchor::Error`, we will reject registering hidden types already commit b54d72264aebf88e1099004c52aac6e4a06affd8 Author: Oli Scherer Date: Wed Feb 21 10:38:54 2024 +0000 Use `DefineOpaqueTypes::Yes` in diagnostics code commit 109daa2d4bafc37b9e86a751b930aa033b7d4b14 Author: Arthur Carcano Date: Wed Apr 3 02:43:54 2024 +0200 Fix diagnostic for qualifier in extern block Closes: https://github.com/rust-lang/rust/issues/123306 commit 0dca136841191074e1703ae9eccc8b15aadb643f Author: Oli Scherer Date: Thu Apr 4 09:46:53 2024 +0000 Try explicitly outlining the panic machinery commit 769ab55558488d1ff786fa13e8ba1fb071a9791b Author: Oli Scherer Date: Tue Apr 2 16:20:04 2024 +0000 Add regression test commit 398a52a8dc6aa96f2c942b24715288f4f96b9c31 Merge: e80ca2f3816 0478d26c8b3 Author: bors Date: Thu Apr 4 09:16:44 2024 +0000 Auto merge of #12340 - not-elm:fix/issue-12334, r=llogiq FIX(12334): manual_swap auto fix Fixed: #12334 Initialization expressions are now generated as needed if the slice index is bound to a variable. ---- changelog: Fix [`manual_swap`] commit 4c6c6298664fff9f3549f05ba2b689bcd30b0fc7 Merge: 29fe618f750 82789763c7c Author: bors Date: Thu Apr 4 08:43:53 2024 +0000 Auto merge of #115538 - lcnr:fn-def-wf, r=compiler-errors check `FnDef` return type for WF better version of #106807, fixes #84533 (mostly). It's not perfect given that we still ignore WF requirements involving bound regions but I wasn't able to quickly write an example, so even if theoretically exploitable, it should be far harder to trigger. This is strictly more restrictive than checking the return type for WF as part of the builtin `FnDef: FnOnce` impl (#106807) and moving to this approach in the future will not break any code. ~~It also agrees with my theoretical view of how this should behave~~ r? types commit c2e4916cf8b28404f60523f22c096652c172070d Author: Ralf Jung Date: Tue Apr 2 08:23:38 2024 +0200 adjust frame_in_std to recognize std tests commit 9e35555474a40d20ebe8d29da56f2d715af21cdd Author: Ralf Jung Date: Sat Mar 30 17:22:28 2024 +0100 smoke-test 'x.py test --miri' on CI commit ecc714d88ee0781192cfedcd7b717a7c0a9a4f05 Author: Ralf Jung Date: Fri Mar 29 19:18:51 2024 +0100 fix parsing the test harness JSON when time could not be measured commit 29fe618f750c5ff7f8fb75871e75280b569b4e67 Merge: 43f4f2a3b1a 473a70de845 Author: bors Date: Thu Apr 4 06:40:30 2024 +0000 Auto merge of #123052 - maurer:addr-taken, r=compiler-errors CFI: Support function pointers for trait methods Adds support for both CFI and KCFI for function pointers to trait methods by attaching both concrete and abstract types to functions. KCFI does this through generation of a `ReifyShim` on any function pointer for a method that could go into a vtable, and keeping this separate from `ReifyShim`s that are *intended* for vtable us by setting a `ReifyReason` on them. CFI does this by setting both the concrete and abstract type on every instance. This should land after #123024 or a similar PR, as it diverges the implementation of CFI vs KCFI. r? `@compiler-errors` commit d99c775febff75f578437504fed69c9f4df5e92d Author: lcnr Date: Tue Apr 2 13:20:03 2024 +0200 unconstrained `NormalizesTo` term for opaques commit 43f4f2a3b1a3d3fb3dbbbe4fde33fb97c780ee98 Merge: 0accf4ec4c0 f090de88759 Author: bors Date: Thu Apr 4 04:36:12 2024 +0000 Auto merge of #119820 - lcnr:leak-check-2, r=jackh726 instantiate higher ranked goals outside of candidate selection This PR modifies `evaluate` to more eagerly instantiate higher-ranked goals, preventing the `leak_check` during candidate selection from detecting placeholder errors involving that binder. For a general background regarding higher-ranked region solving and the leak check, see https://hackmd.io/qd9Wp03cQVy06yOLnro2Kg. > The first is something called the **leak check**. You can think of it as a "quick and dirty" approximation for the region check, which will come later. The leak check detects some kinds of errors early, essentially deciding between "this set of outlives constraints are guaranteed to result in an error eventually" or "this set of outlives constraints may be solvable". ## The ideal future We would like to end up with the following idealized design to handle universal binders: ```rust fn enter_forall<'tcx, T, R>( forall: Binder<'tcx, T>, f: impl FnOnce(T) -> R, ) -> R { let new_universe = infcx.increment_universe_index(); let value = instantiate_binder_with_placeholders_in(new_universe, forall); let result = f(value); eagerly_handle_higher_ranked_region_constraints_in(new_universe); infcx.decrement_universe_index(); assert!(!result.has_placeholders_in_or_above(new_universe)); result } ``` That is, when universally instantiating a binder, anything using the placeholders has to happen inside of a limited scope (the closure `f`). After this closure has completed, all constraints involving placeholders are known. We then handle any *external constraints* which name these placeholders. We destructure `TypeOutlives` constraints involving placeholders and eagerly handle any region constraints involving these placeholders. We do not return anything mentioning the placeholders created inside of this function to the caller. Being able to eagerly handle *all* region constraints involving placeholders will be difficult due to complex `TypeOutlives` constraints, involving inference variables or alias types, and higher ranked implied bounds. The exact issues and possible solutions are out of scope of this FCP. #### How does the leak check fit into this The `leak_check` is an underapproximation of `eagerly_handle_higher_ranked_region_constraints_in`. It detects some kinds of errors involving placeholders from `new_universe`, but not all of them. It only looks at region outlives constraints, ignoring `TypeOutlives`, and checks whether one of the following two conditions are met for **placeholders in or above `new_universe`**, in which case it results in an error: - `'!p1: '!p2` a placeholder `'!p2` outlives a different placeholder `'!p1` - `'!p1: '?2` an inference variable `'?2` outlives a placeholder `'!p1` *which it cannot name* It does not handle all higher ranked region constraints, so we still return constraints involving placeholders from `new_universe` which are then (re)checked by `lexical_region_resolve` or MIR borrowck. As we check higher ranked constraints in the full regionck anyways, the `leak_check` is not soundness critical. It's current only purpose is to move some higher ranked region errors earlier, enabling it to guide type inference and trait solving. Adding additional uses of the `leak_check` in the future would only strengthen inference and is therefore not breaking. ## Where do we use currently use the leak check The `leak_check` is currently used in two places: Coherence does not use a proper regionck, only relying on the `leak_check` called [at the end of the implicit negative overlap check](https://github.com/rust-lang/rust/blob/8b94152af68a0ed6d6af0b5ba57491e40481008e/compiler/rustc_trait_selection/src/traits/coherence.rs#L235-L238). During coherence all parameters are instantiated with inference variables, so the only possible region errors are higher-ranked. We currently also sometimes make guesses when destructuring `TypeOutlives` constraints which can theoretically result in incorrect errors. This could result in overlapping impls. We also use the `leak_check` [at the end of `fn evaluation_probe`](https://github.com/rust-lang/rust/blob/8b94152af68a0ed6d6af0b5ba57491e40481008e/compiler/rustc_trait_selection/src/traits/select/mod.rs#L607-L610). This function is used during candidate assembly for `Trait` goals. Most notably we use [inside of `evaluate_candidate` during winnowing](https://github.com/rust-lang/rust/blob/0e4243538b9119654c22dce688f8a63c81864de9/compiler/rustc_trait_selection/src/traits/select/mod.rs#L491-L502). Conceptionally, it is as if we compute each candidate in a separate `enter_forall`. ## The current use in `fn evaluation_probe` is undesirable Because we only instantiate a higher-ranked goal once inside of `fn evaluation_probe`, errors involving placeholders from that binder can impact selection. This results in inconsistent behavior ([playground]( *[playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=dac60ebdd517201788899ffa77364831)*)): ```rust trait Leak<'a> {} impl Leak<'_> for Box {} impl Leak<'static> for Box {} fn impls_leak Leak<'a>>() {} trait IndirectLeak<'a> {} impl<'a, T: Leak<'a>> IndirectLeak<'a> for T {} fn impls_indirect_leak IndirectLeak<'a>>() {} fn main() { // ok // // The `Box` impls fails the leak check, // meaning that we apply the `Box` impl. impls_leak::>(); // error: type annotations needed // // While the `Box` impl would fail the leak check // we have already instantiated the binder while applying // the generic `IndirectLeak` impl, so during candidate // selection of `Leak` we do not detect the placeholder error. // Evaluation of `Box<_>: Leak<'!a>` is therefore ambiguous, // resulting in `for<'a> Box<_>: Leak<'a>` also being ambiguous. impls_indirect_leak::>(); } ``` We generally prefer `where`-bounds over implementations during candidate selection, both for [trait goals](https://github.com/rust-lang/rust/blob/11f32b73e0dc9287e305b5b9980d24aecdc8c17f/compiler/rustc_trait_selection/src/traits/select/mod.rs#L1863-L1887) and during [normalization](https://github.com/rust-lang/rust/blob/11f32b73e0dc9287e305b5b9980d24aecdc8c17f/compiler/rustc_trait_selection/src/traits/project.rs#L184-L198). However, we currently **do not** use the `leak_check` during candidate assembly in normalizing. This can result in inconsistent behavior: ```rust trait Trait<'a> { type Assoc; } impl<'a, T> Trait<'a> for T { type Assoc = usize; } fn trait_bound Trait<'a>>() {} fn projection_bound Trait<'a, Assoc = usize>>() {} // A function with a trivial where-bound which is more // restrictive than the impl. fn function>() { // ok // // Proving `for<'a> T: Trait<'a>` using the where-bound results // in a leak check failure, so we use the more general impl, // causing this to succeed. trait_bound::(); // error // // Proving the `Projection` goal `for<'a> T: Trait<'a, Assoc = usize>` // does not use the leak check when trying the where-bound, causing us // to prefer it over the impl, resulting in a placeholder error. projection_bound::(); // error // // Trying to normalize the type `for<'a> fn(>::Assoc)` // only gets to `>::Assoc` once `'a` has been already // instantiated, causing us to prefer the where-bound over the impl // resulting in a placeholder error. Even if were were to also use the // leak check during candidate selection for normalization, this // case would still not compile. let _higher_ranked_norm: for<'a> fn(>::Assoc) = |_| (); } ``` This is also likely to be more performant. It enables more caching in the new trait solver by simply [recursively calling the canonical query][new solver] after instantiating the higher-ranked goal. It is also unclear how to add the leak check to normalization in the new solver. To handle https://github.com/rust-lang/trait-system-refactor-initiative/issues/1 `Projection` goals are implemented via `AliasRelate`. This again means that we instantiate the binder before ever normalizing any alias. Even if we were to avoid this, we lose the ability to [cache normalization by itself, ignoring the expected `term`](https://github.com/rust-lang/rust/blob/5bd5d214effd494f4bafb29b3a7a2f6c2070ca5c/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs#L34-L49). We cannot replace the `term` with an inference variable before instantiating the binder, as otherwise `for<'a> T: Trait = &'a ()>` breaks. If we only replace the term after instantiating the binder, we cannot easily evaluate the goal in a separate context, as [we'd then lose the information necessary for the leak check](https://github.com/rust-lang/rust/blob/11f32b73e0dc9287e305b5b9980d24aecdc8c17f/compiler/rustc_next_trait_solver/src/canonicalizer.rs#L230-L232). Adding this information to the canonical input also seems non-trivial. ## Proposed solution I propose to instantiate the binder outside of candidate assembly, causing placeholders from higher-ranked goals to get ignored while selecting their candidate. This mostly[^1] matches the [current behavior of the new solver][new solver]. The impact of this change is therefore as follows: ```rust trait Leak<'a> {} impl Leak<'_> for Box {} impl Leak<'static> for Box {} fn impls_leak Leak<'a>>() {} trait IndirectLeak<'a> {} impl<'a, T: Leak<'a>> IndirectLeak<'a> for T {} fn impls_indirect_leak IndirectLeak<'a>>() {} fn guide_selection() { // ok -> ambiguous impls_leak::>(); // ambiguous impls_indirect_leak::>(); } trait Trait<'a> { type Assoc; } impl<'a, T> Trait<'a> for T { type Assoc = usize; } fn trait_bound Trait<'a>>() {} fn projection_bound Trait<'a, Assoc = usize>>() {} // A function which a trivial where-bound which is more // restrictive than the impl. fn function>() { // ok -> error trait_bound::(); // error projection_bound::(); // error let _higher_ranked_norm: for<'a> fn(>::Assoc) = |_| (); } ``` This does not change the behavior if candidates have higher ranked nested goals, as in this case the `leak_check` causes the nested goal to result in an error ([playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=a74c25300b23db9022226de99d8a2fa6)): ```rust trait LeakCheckFailure<'a> {} impl LeakCheckFailure<'static> for () {} trait Trait {} impl Trait for () where for<'a> (): LeakCheckFailure<'a> {} impl Trait for () {} fn impls_trait, U>() {} fn main() { // ok // // It does not matter whether candidate assembly // considers the placeholders from higher-ranked goal. // // Either `for<'a> (): LeakCheckFailure<'a>` has no // applicable candidate or it has a single applicable candidate // when then later results in an error. This allows us to // infer `U` to `u16`. impls_trait::<(), _>() } ``` ## Impact on existing crates This is a **breaking change**. [A crater run](https://github.com/rust-lang/rust/pull/119820#issuecomment-1926862174) found 17 regressed crates with 7 root causes. For a full analysis of all affected crates, see https://gist.github.com/lcnr/7c1c652f30567048ea240554a36ed95c. --- I believe this breakage to be acceptable and would merge this change. I am confident that the new position of the leak check matches our idealized future and cannot envision any other consistent alternative. Where possible, I intend to open PRs fixing/avoiding the regressions before landing this PR. I originally intended to remove the `coherence_leak_check` lint in the same PR. However, while I am confident in the *position* of the leak check, deciding on its exact behavior is left as future work, cc #112999. This PR therefore only moves the leak check while keeping the lint when relying on it in coherence. [new solver]: https://github.com/rust-lang/rust/blob/master/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs#L479-L484 [^1]: the new solver has a separate cause of inconsistent behavior rn https://github.com/rust-lang/trait-system-refactor-initiative/issues/53#issuecomment-1914310171 r? `@nikomatsakis` commit b0b7c860e13858cbc344dcf2baf860752e3a2dd3 Author: Ben Kimock Date: Thu Apr 4 00:10:01 2024 -0400 Teach MIR inliner query cycle avoidance about const_eval_select commit 0accf4ec4c07d23aa86f6a97aeb8797941abc30e Merge: b4acbe4233b 4332498a6da Author: bors Date: Thu Apr 4 02:11:23 2024 +0000 Auto merge of #123440 - jhpratt:rollup-yat6crk, r=jhpratt Rollup of 4 pull requests Successful merges: - #122356 (std::rand: fix dragonflybsd after #121942.) - #123093 (Add a nice header to our README.md) - #123307 (Fix f16 and f128 feature gating on different editions) - #123401 (Check `x86_64` size assertions on `aarch64`, too) r? `@ghost` `@rustbot` modify labels: rollup commit 82789763c7c5c09c6b0481d18cf00ef67b4b6fa3 Author: Boxy Date: Thu Apr 4 02:14:52 2024 +0100 rebase commit 2b67f0104a9fcdb3a6aa41bceb33e62e609e6b6c Author: lcnr Date: Mon Sep 4 17:35:51 2023 +0200 check `FnDef` return type for WF commit e5376f3947ba8faf0f7c3a9543366060d662357d Author: Jules Bertholet Date: Wed Apr 3 20:35:02 2024 -0400 Address final nits commit 4332498a6daff23e4403d9bce43d97ed63cad382 Merge: 819568a7b41 2d47cd77ac7 Author: Jacob Pratt Date: Wed Apr 3 20:17:06 2024 -0400 Rollup merge of #123401 - Zalathar:assert-size-aarch64, r=fmease Check `x86_64` size assertions on `aarch64`, too (Context: https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Checking.20size.20assertions.20on.20aarch64.3F) Currently the compiler has around 30 sets of `static_assert_size!` for various size-critical data structures (e.g. various IR nodes), guarded by `#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]`. (Presumably this cfg avoids having to maintain separate size values for 32-bit targets and unusual 64-bit targets. Apparently it may have been necessary before the i128/u128 alignment changes, too.) This is slightly incovenient for people on aarch64 workstations (e.g. Macs), because the assertions normally aren't checked until we push to a PR. So this PR adds `aarch64` to the `#[cfg(..)]` guarding all of those assertions in the compiler. --- Implemented with a simple find/replace. Verified by manually inspecting each `static_assert_size!` in `compiler/`, and checking that either the replacement succeeded, or adding aarch64 wouldn't have been appropriate. commit 819568a7b41cf5b453df675b41569b311216460c Merge: c8cd010616d 5afe072ead1 Author: Jacob Pratt Date: Wed Apr 3 20:17:05 2024 -0400 Rollup merge of #123307 - tgross35:f16-f128-feature-gate-fix, r=petrochenkov Fix f16 and f128 feature gating on different editions Apply the fix from https://github.com/rust-lang/rust/issues/123282#issuecomment-2035063388 to correctly gates `f16` and `f128` in editions other than 2015 commit c8cd010616d9dfa49864527388e7c065937e94c9 Merge: 875d2547500 ce4cc9df960 Author: Jacob Pratt Date: Wed Apr 3 20:17:05 2024 -0400 Rollup merge of #123093 - Urgau:improve-readme, r=workingjubilee Add a nice header to our README.md This PR improves our README, it is greatly inspired by [esbuild](https://github.com/evanw/esbuild/blob/9d1777f23d9b64c186345223d92f319e59388d8b/README.md) README. Context: As was reading https://johnjago.com/great-docs/ I though we could greatly improve ours. So that's what I did. It provides direct "quick-access" links to pages in rust-lang.org. The "Why Rust?" section is ~~a direct copy/paste of the same~~ modified version of section in [rust-lang.org](https://www.rust-lang.org/). | Before | After | |--------|-------| | ![before](https://github.com/rust-lang/rust/assets/3616612/4afb6753-18a9-4881-919e-2a79328beb5a) | ![after](https://github.com/rust-lang/rust/assets/3616612/408dea7b-5d97-4bb8-a77e-35541ddd50cb)
![after](https://github.com/rust-lang/rust/assets/3616612/76e4a8c6-b61c-46e8-b5ba-4f09c13cfc94)
![after -1](https://github.com/rust-lang/rust/assets/3616612/ed50675c-8301-457c-8b9a-a1199c515fb7)
| Note: I removed the manual TOC, since GitHub provides it's own at the top right corner and I don't think it's needed anymore. Same for the notice about the readme being for users, it's now clear enough and that notice was distracting anyway. commit 875d2547500f9c588c0537ab12672b4cf844eca4 Merge: 4fd4797c265 6a16638de64 Author: Jacob Pratt Date: Wed Apr 3 20:17:04 2024 -0400 Rollup merge of #122356 - devnexen:dfbsd_build_fix, r=jhpratt std::rand: fix dragonflybsd after #121942. commit b4acbe4233b97ec51605c513ce6b215470574f80 Merge: 4fd4797c265 bd8ca780a51 Author: bors Date: Thu Apr 4 00:09:02 2024 +0000 Auto merge of #123240 - compiler-errors:assert-args-compat, r=fmease Assert that args are actually compatible with their generics, rather than just their count Right now we just check that the number of args is right, rather than actually checking the kinds. Uplift a helper fn that I wrote from trait selection to do just that. Found a couple bugs along the way. r? `@lcnr` or `@fmease` (or anyone really lol) commit e08fdb0f2fcb4cabb430b0512331bb781b4ea653 Author: Zalathar Date: Thu Apr 4 11:04:32 2024 +1100 coverage: Remove useless constants commit f6b97ef5e567f9893cd58b6fd00fee033593bd59 Author: Zalathar Date: Thu Apr 4 10:55:20 2024 +1100 Manually run `clang-format` on `CoverageMappingWrapper.cpp` commit f090de88759ed9abc65074ff9926e03a3d550d77 Author: Boxy Date: Wed Apr 3 22:48:48 2024 +0100 rebase oddity commit f029602920638d0f73488e2517ce7547d287a14b Author: Michael Goulet Date: Wed Apr 3 17:41:03 2024 -0400 Tests for getting parent of synthetic HIR commit 4fa5fb684e891978303db0b8f100fab1e19eb06d Author: lcnr Date: Wed Apr 3 22:27:13 2024 +0100 move leak check out of candidate evaluation this prevents higher ranked goals from guiding selection commit e2ebaa1a08aff195487cd7afacdaf7fb0ef62aa7 Author: Nadrieril Date: Wed Apr 3 21:17:36 2024 +0200 Add `if let` tests commit fbc56dfac13ba6fb81bca6439e120a9c554c3a57 Author: Slanterns Date: Thu Apr 4 05:04:27 2024 +0800 Stabilize `Literal::c_string` commit 61ac7812c65601695460a1ab6a4999d35efb66cc Author: Slanterns Date: Thu Apr 4 05:00:49 2024 +0800 Stabilize `Literal::byte_character` commit 4fd4797c2654977f545c9a91e2aa4e6cdbb38919 Merge: 98efd808e1b 65398c46b82 Author: bors Date: Wed Apr 3 20:19:51 2024 +0000 Auto merge of #123429 - matthiaskrgr:rollup-4emw4e9, r=matthiaskrgr Rollup of 8 pull requests Successful merges: - #121595 (Better reporting on generic argument mismatchs) - #122619 (Fix some unsoundness with PassMode::Cast ABI) - #122964 (Rename `expose_addr` to `expose_provenance`) - #123291 (Move some tests) - #123301 (pattern analysis: fix union handling) - #123395 (More postfix match fixes) - #123419 (rustc_index: Add a `ZERO` constant to index types) - #123421 (Fix target name in NetBSD platform-support doc) r? `@ghost` `@rustbot` modify labels: rollup commit 6728f2fef4286d7060feb7667b5da9ef3f512e34 Merge: b0710dc5f5b 46fc398706d Author: Matthias Krüger Date: Wed Apr 3 22:11:02 2024 +0200 Rollup merge of #123419 - petrochenkov:zeroindex, r=compiler-errors rustc_index: Add a `ZERO` constant to index types It is commonly used. commit 65398c46b8207fb36d85bdab1ff2d68c38f0e3a4 Merge: 25b0e841705 6edb021fd36 Author: Matthias Krüger Date: Wed Apr 3 22:11:02 2024 +0200 Rollup merge of #123421 - taiki-e:netbsd-doc, r=Nilstrieb Fix target name in NetBSD platform-support doc NetBSD platform-support doc currently mentions `amd64-unknown-netbsd`, but it is not a valid target name (the correct name is `x86_64-unknown-netbsd`). https://github.com/rust-lang/rust/blob/ceab6128fa48a616bfd3e3adf4bc80133b8ee223/src/doc/rustc/src/platform-support/netbsd.md?plain=1#L16 ```console $ rustc --print target-list | grep netbsd aarch64-unknown-netbsd aarch64_be-unknown-netbsd armv6-unknown-netbsd-eabihf armv7-unknown-netbsd-eabihf i586-unknown-netbsd i686-unknown-netbsd mipsel-unknown-netbsd powerpc-unknown-netbsd riscv64gc-unknown-netbsd sparc64-unknown-netbsd x86_64-unknown-netbsd ``` commit 25b0e841705616ef103626be338289ceaafd41be Merge: 202509b427a b40ea03f8a9 Author: Matthias Krüger Date: Wed Apr 3 22:11:02 2024 +0200 Rollup merge of #123419 - petrochenkov:zeroindex, r=compiler-errors rustc_index: Add a `ZERO` constant to index types It is commonly used. commit 202509b427aa7c8fdbd7971311b631b4cbf6cd0d Merge: 5f74403c8ef 4cb5643bd4b Author: Matthias Krüger Date: Wed Apr 3 22:11:01 2024 +0200 Rollup merge of #123395 - compiler-errors:postfix-matches-fixes-2, r=petrochenkov More postfix match fixes These affect diagnostics only, as far as I can tell. I'm too lazy to come up with UI tests, but I could be convinced otherwise. Specifically, I think changing the precedence computation actually doesn't change anything, but tweaking `contains_exterior_struct_lit` does mean that some diagnostics will begin parenthesizing `S {}.match {}`. commit 5f74403c8ef13f7af4275c078aaa0db31679f470 Merge: 0c8c18fcc6f 27704c7f9ee Author: Matthias Krüger Date: Wed Apr 3 22:11:01 2024 +0200 Rollup merge of #123301 - Nadrieril:unions, r=compiler-errors pattern analysis: fix union handling Little known fact: rust supports union patterns. Exhaustiveness handles them soundly but reports nonsensical missing patterns. This PR fixes the reported patterns and documents what we're doing. r? `@compiler-errors` commit 0c8c18fcc6f1a717f6af6a7c3b6a27421c24da85 Merge: 80d592cc24d 4c0aea0d47d Author: Matthias Krüger Date: Wed Apr 3 22:11:01 2024 +0200 Rollup merge of #123291 - c410-f3r:testsssssss, r=petrochenkov Move some tests r? `@petrochenkov` commit 80d592cc24d295e2b00e44e7c3d03725a2b80d96 Merge: bc8415b9e69 989660c3e6e Author: Matthias Krüger Date: Wed Apr 3 22:11:00 2024 +0200 Rollup merge of #122964 - joboet:pointer_expose, r=Amanieu Rename `expose_addr` to `expose_provenance` `expose_addr` is a bad name, an address is just a number and cannot be exposed. The operation is actually about the provenance of the pointer. This PR thus changes the name of the method to `expose_provenance` without changing its return type. There is sufficient precedence for returning a useful value from an operation that does something else without the name indicating such, e.g. [`Option::insert`](https://doc.rust-lang.org/nightly/std/option/enum.Option.html#method.insert) and [`MaybeUninit::write`](https://doc.rust-lang.org/nightly/std/mem/union.MaybeUninit.html#method.write). Returning the address is merely convenient, not a fundamental part of the operation. This is implied by the fact that integers do not have provenance since ```rust let addr = ptr.addr(); ptr.expose_provenance(); let new = ptr::with_exposed_provenance(addr); ``` must behave exactly like ```rust let addr = ptr.expose_provenance(); let new = ptr::with_exposed_provenance(addr); ``` as the result of `ptr.expose_provenance()` and `ptr.addr()` is the same integer. Therefore, this PR removes the `#[must_use]` annotation on the function and updates the documentation to reflect the important part. ~~An alternative name would be `expose_provenance`. I'm not at all opposed to that, but it makes a stronger implication than we might want that the provenance of the pointer returned by `ptr::with_exposed_provenance`[^1] is the same as that what was exposed, which is not yet specified as such IIUC. IMHO `expose` does not make that connection.~~ A previous version of this PR suggested `expose` as name, libs-api [decided on](https://github.com/rust-lang/rust/pull/122964#issuecomment-2033194319) `expose_provenance` to keep the symmetry with `with_exposed_provenance`. CC `@RalfJung` r? libs-api [^1]: I'm using the new name for `from_exposed_addr` suggested by #122935 here. commit bc8415b9e699b870f425e1f424224c039747f67a Merge: 32c8c5cb7ee dec81ac223a Author: Matthias Krüger Date: Wed Apr 3 22:11:00 2024 +0200 Rollup merge of #122619 - erikdesjardins:cast, r=compiler-errors Fix some unsoundness with PassMode::Cast ABI Fixes #122617 Reviewable commit-by-commit. More info in each commit message. commit 32c8c5cb7ee793fe318d04a33b05b53320fb3aae Merge: 703dc9ce64d 8a5245e7dd0 Author: Matthias Krüger Date: Wed Apr 3 22:10:59 2024 +0200 Rollup merge of #121595 - strottos:issue_116615, r=compiler-errors Better reporting on generic argument mismatchs This allows better reporting as per issue #116615 . If you have a function: ``` fn foo(a: T, b: T) {} ``` and call it like so: ``` foo(1, 2.) ``` it'll give improved error reported similar to the following: ``` error[E0308]: mismatched types --> generic-mismatch-reporting-issue-116615.rs:6:12 | 6 | foo(1, 2.); | --- - ^^ expected integer, found floating-point number | | | | | expected argument `b` to be an integer because that argument needs to match the type of this parameter | arguments to this function are incorrect | note: function defined here --> generic-mismatch-reporting-issue-116615.rs:1:4 | 1 | fn foo(a: T, b: T) {} | ^^^ - ---- ---- | | | | | | | this parameter needs to match the integer type of `a` | | `b` needs to match the type of this parameter | `a` and `b` all reference this parameter T ``` Open question, do we need to worry about error message translation into other languages? Not sure what the status of that is in Rust. NB: Needs some checking over and some tests have altered that need sanity checking, but overall this is starting to get somewhere now. Will take out of draft PR status when this has been done, raising now to allow feedback at this stage, probably 90% ready. commit 5afe072ead1154f9817ee610e3e60345507c7625 Author: Trevor Gross Date: Wed Apr 3 14:17:23 2024 -0400 Fix f16 and f128 feature gates in editions other than 2015 Fixes https://github.com/rust-lang/rust/issues/123282 Co-authored-by: Vadim Petrochenkov commit 9a7b1762279eaa95d0289760d3b859fbbc9221f1 Author: Trevor Gross Date: Sun Mar 31 19:23:57 2024 -0500 Update f16 and f128 tests to run on both 2015 and 2018 editions Reproduce the bug from that indicates this feature gate hits edition-dependent resolution paths. Resolution changed in edition 2018, so test that as well. commit e80ca2f3816ed6f4584480d40b8671a15b2225fc Merge: f9f854f4283 571118f4b01 Author: bors Date: Wed Apr 3 19:07:51 2024 +0000 Auto merge of #12615 - Kobzol:fix-recursive-clone-from, r=blyxyas Do not suggest `assigning_clones` in `Clone` impl This PR modifies `assigning_clones` to detect situations where the `clone` call is inside a `Clone` impl, and avoids suggesting the lint in such situations. r? `@blyxyas` Fixes: https://github.com/rust-lang/rust-clippy/issues/12600 changelog: Do not invoke `assigning_clones` inside `Clone` impl commit 8021192d340730430810589b453f9cf047bbc9b5 Author: Nadrieril Date: Mon Apr 1 16:47:58 2024 +0200 More precise false edges commit 8f80259f105adad3af1a4abe00470178a987e3cb Author: Nadrieril Date: Sun Mar 10 17:54:40 2024 +0100 Explain false edges in more detail commit 50103ab14d678c1c04a3db5621072bb1f8c2d860 Author: Nadrieril Date: Sun Mar 10 14:15:03 2024 +0100 Add tests commit 571118f4b018aad8b15e8f80aa905f0209a27aba Author: Jakub Beránek Date: Tue Apr 2 10:41:58 2024 +0200 Do not suggest `assigning_clones` in `Clone` impl commit a6803b9de4e7ab146b442162316b340f887a7796 Author: Ralf Jung Date: Mon Apr 1 23:56:59 2024 +0200 add 'x.py miri', and make it work for 'library/{core,alloc,std}' commit 98efd808e1b77cd70a097620aad6250727167a28 Merge: 703dc9ce64d 44b36024786 Author: bors Date: Wed Apr 3 18:18:44 2024 +0000 Auto merge of #123415 - petrochenkov:parenting, r=compiler-errors hir: Drop owner's own item-local id (zero) from parenting tables I expect this to be a common case. commit 64d6da5cda286b083fe0c02b81baeaf7855b99cd Merge: d9f29fa018b 4e4de3f5b77 Author: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed Apr 3 19:54:56 2024 +0200 Merge pull request #1476 from taiki-e/unchecked-shift Fix ICE on unchecked shift commit ce4cc9df960ec5c0a840d75e7e57284bfd80d25d Author: Zander Milroy Date: Tue Apr 2 20:05:01 2024 -0400 Use SVG logos in the README.md. These should likely eventually reference the rust-lang.org site instead of raw.githubusercontent.com commit 6edb021fd3655df3848f590005e3a688147d2164 Author: Taiki Endo Date: Thu Apr 4 01:31:04 2024 +0900 Fix target name in NetBSD platform-support doc commit 703dc9ce64d9b31a239a7280d9b5f9ddd85ffed6 Merge: ceab6128fa4 658c8f73677 Author: bors Date: Wed Apr 3 16:16:52 2024 +0000 Auto merge of #123416 - matthiaskrgr:rollup-j85wj05, r=matthiaskrgr Rollup of 7 pull requests Successful merges: - #123209 (Add section to sanitizer doc for `-Zexternal-clangrt`) - #123342 (x.py test: remove no-op --skip flag) - #123382 (Assert `FnDef` kind) - #123386 (Set `CARGO` instead of `PATH` for Rust Clippy) - #123393 (rustc_ast: Update `P` docs to reflect mutable status.) - #123394 (Postfix match fixes) - #123412 (Output URLs of CI artifacts to GitHub summary) r? `@ghost` `@rustbot` modify labels: rollup commit b6486fae5c9ce3610aa325a3c86d23871e05c1e8 Author: Francisco Salgueiro Date: Wed Apr 3 17:07:12 2024 +0100 add missing links in development guide commit 46fc398706da87768f1f8ea1cdb9af2e17512a68 Author: Vadim Petrochenkov Date: Wed Apr 3 17:49:59 2024 +0300 rustc_index: Add a `ZERO` constant to index types It is commonly used. commit b40ea03f8a9a94c294679b9f261b86ded120454f Author: Vadim Petrochenkov Date: Wed Apr 3 17:49:59 2024 +0300 rustc_index: Add a `ZERO` constant to index types It is commonly used. commit bd8ca780a513a42fe635c6d099ec6f4ba5c22221 Author: Michael Goulet Date: Sat Mar 30 12:34:15 2024 -0400 Fix up error message for debug_assert_args_compat for IATs commit 03163ef9d99a2717edd976174a847293b6f1c8cb Author: Michael Goulet Date: Sat Mar 30 12:17:21 2024 -0400 Don't fill non-ty args with ty::Error commit 657dadf2cc20d7d48643a300c23388ef026c8c83 Author: Michael Goulet Date: Sat Mar 30 12:10:47 2024 -0400 Simplify some cfging commit c9f852979359779ec1c91b91eb477272308a864f Author: Michael Goulet Date: Sat Mar 30 12:05:14 2024 -0400 Uplift and start using check_args_compatible more liberally commit e3025d6a55b1381802e8d6a85cbd4491dcaaa81d Author: Michael Goulet Date: Sat Mar 30 12:03:29 2024 -0400 Stop chopping off args for no reason commit 658c8f73677696ff2f37ea1cbd5d667d2562ccbf Merge: deb48aa0f52 508530dbc5b Author: Matthias Krüger Date: Wed Apr 3 17:15:50 2024 +0200 Rollup merge of #123412 - Kobzol:ci-artifacts-in-summary, r=Mark-Simulacrum Output URLs of CI artifacts to GitHub summary I often want to download CI artifacts published from our workflows (I suspect others might do the same), but it's a bit annoying to extract their links from the CI logs currently. This PR also outputs URLs to them to the GitHub Actions summaries. r? `@Mark-Simulacrum` commit deb48aa0f528a1e0b3d73c0ca21122d8c018c0ad Merge: 0c11f13a577 bed8b70d674 Author: Matthias Krüger Date: Wed Apr 3 17:15:50 2024 +0200 Rollup merge of #123394 - compiler-errors:postfix-match-fixes, r=estebank Postfix match fixes 1. Don't ice on `expr as Ty.match {}` 2. Fix the suggestion span for non-exhaustive matches to add `_ => todo!(),` Fixes #123383 commit 0c11f13a577a6b0e2fee097b52ce348c244f09ad Merge: 29c72ce1838 5075931290a Author: Matthias Krüger Date: Wed Apr 3 17:15:49 2024 +0200 Rollup merge of #123393 - aDotInTheVoid:ast-p-docs, r=Nilstrieb rustc_ast: Update `P` docs to reflect mutable status. `P` has implemented `DerefMut` since #54277. While this was lamented at the time [1], rustc now relies on it extensively via the many implementors of MutVisitor [2]. Updates the docs to reflect that `P` is fundamentally mutable, and a few other cleanups to make them nicer to browse. [1]: https://github.com/rust-lang/rust/pull/54277#discussion_r257181754 [2]: https://doc.rust-lang.org/1.77.1/nightly-rustc/rustc_ast/mut_visit/trait.MutVisitor.html#implementors r? `@Nilstrieb` commit 29c72ce18382c030f5f3124808dcbd7695397439 Merge: 94c402733d8 66dee4a9aa7 Author: Matthias Krüger Date: Wed Apr 3 17:15:49 2024 +0200 Rollup merge of #123386 - Rajveer100:branch-for-issue-123227, r=onur-ozkan Set `CARGO` instead of `PATH` for Rust Clippy Resolves #123227 Previously, clippy was using `cargo` from `PATH`, but since [PR](https://github.com/rust-lang/rust-clippy/pull/11944), it now prioritises checking `CARGO` first. commit 94c402733d8bca4675fc8040cd968ecc5e5387a0 Merge: f7c34c03bed cc5105d246c Author: Matthias Krüger Date: Wed Apr 3 17:15:48 2024 +0200 Rollup merge of #123382 - compiler-errors:assert-fndef-kind, r=fmease Assert `FnDef` kind Only found one bug, where we were using the variant def id rather than its ctor def id to make the `FnDef` for a `type_of` r? fmease commit f7c34c03bed369b069cf37c581676d0f9ee2c975 Merge: 1bde86b18b8 5a887942a5e Author: Matthias Krüger Date: Wed Apr 3 17:15:48 2024 +0200 Rollup merge of #123342 - RalfJung:noskip, r=onur-ozkan x.py test: remove no-op --skip flag None of the test commands seems to do anything with this flag, so we might as well remove it. commit 1bde86b18b8bc2d34cbe436b17a2010e396a97f6 Merge: ceab6128fa4 382459e0478 Author: Matthias Krüger Date: Wed Apr 3 17:15:47 2024 +0200 Rollup merge of #123209 - ObsidianMinor:doc/external-clangrt, r=michaelwoerister Add section to sanitizer doc for `-Zexternal-clangrt` After spending a week looking for answers to how to do the very thing this flag lets me do, it felt appropriate to document it where I would've expected it to be. commit 44b36024786a5e00d55111c1a43a0c8388caefcb Author: Vadim Petrochenkov Date: Wed Apr 3 17:23:08 2024 +0300 hir: Drop owner's own item-local id (zero) from parenting tables commit 53e31dc45cecb671c664dd13685cfa9dc832b336 Author: joboet Date: Wed Apr 3 15:17:00 2024 +0200 rename `expose_addr` to `expose_provenance` commit b0710dc5f5be62a2f5151642a71edb54b6ee8728 Author: joboet Date: Wed Apr 3 15:17:00 2024 +0200 rename `expose_addr` to `expose_provenance` commit 989660c3e6efc1c5eb2e822f68863df7d06cbcb4 Author: joboet Date: Wed Apr 3 15:17:00 2024 +0200 rename `expose_addr` to `expose_provenance` commit 508530dbc5b6a1234b1616a077efbfd048c3a3cd Author: Jakub Beránek Date: Wed Apr 3 15:28:47 2024 +0200 Output URLs of CI artifacts to GitHub summary commit ceab6128fa48a616bfd3e3adf4bc80133b8ee223 Merge: 99c42d23406 049a9175359 Author: bors Date: Wed Apr 3 12:32:34 2024 +0000 Auto merge of #123390 - tgross35:f16-f128-libs-basic-impls-bootstrap, r=jhpratt Put basic impls for f16 and f128 behind cfg(not(bootstrap)) We will lose `f16` and `f128` in the beta compiler after the revert for lands. Change what was added in to be behind `#[cfg(not(bootstrap))]` to account for this. commit 4e4de3f5b771cac99593f2421a122a7bc6e06ffb Author: Taiki Endo Date: Wed Apr 3 21:30:19 2024 +0900 Fix ICE on unchecked shift commit 2815edc671357649438dfac1bf3aca3aba07b71b Author: Guillaume Gomez Date: Wed Apr 3 14:27:11 2024 +0200 Update `rustdoc_css_themes.rs` to take into account new selectors commit 8f9d93bf57cb1c74cd52581a980bc3accd01e5c4 Author: Guillaume Gomez Date: Wed Apr 3 14:11:23 2024 +0200 Add GUI test to ensure there is always a theme applied if JS is disabled commit 2edc54c830bb3925967c77486fef32707b4321ce Author: Guillaume Gomez Date: Wed Apr 3 14:11:01 2024 +0200 Default to light theme is JS is enabled but not working commit 66dee4a9aa759748319f4e9c81dbb87d7e759ee0 Author: Rajveer Date: Wed Apr 3 00:49:43 2024 +0530 Set `CARGO` instead of `PATH` for Rust Clippy Resolves #123227 commit 99c42d234064bede688a02d7076d369ecce1a513 Merge: c7491b97331 a277c901d95 Author: bors Date: Wed Apr 3 10:30:34 2024 +0000 Auto merge of #123322 - matthewjasper:remove-mir-unsafeck, r=lcnr,compiler-errors Remove MIR unsafe check Now that THIR unsafeck is enabled by default in stable I think we can remove MIR unsafeck entirely. This PR also removes safety information from MIR. commit a277c901d95c5fcbb3dbd6608731eebb9e1a01ce Author: Matthew Jasper Date: Tue Feb 27 11:57:52 2024 +0000 Remove MIR unsafe check This also remove safety information from MIR. commit 5075931290ac4939b1645d2a9cde06cce240ba59 Author: Alona Enraght-Moony Date: Tue Apr 2 22:46:00 2024 +0000 rustc_ast: Update `P` docs to reflect mutable status. `P` has implemented `DerefMut` since #54277. While this was lamented at the time [1], rustc now relies on it extensively via the many implementors of MutVisitor [2]. Updates the docs to reflect that `P` is fundamentally mutable, and a few other cleanups to make them nicer to browse. [1]: https://github.com/rust-lang/rust/pull/54277#discussion_r257181754 [2]: https://doc.rust-lang.org/1.77.1/nightly-rustc/rustc_ast/mut_visit/trait.MutVisitor.html#implementors commit c7491b97331afb79189d029c7ae7554010a2b187 Merge: 76cf07d5df5 0c0d88864a6 Author: bors Date: Wed Apr 3 08:28:48 2024 +0000 Auto merge of #123402 - workingjubilee:rollup-0j5ihn6, r=workingjubilee Rollup of 4 pull requests Successful merges: - #122411 ( Provide cabi_realloc on wasm32-wasip2 by default ) - #123349 (Fix capture analysis for by-move closure bodies) - #123359 (Link against libc++abi and libunwind as well when building LLVM wrappers on AIX) - #123388 (use a consistent style for links) r? `@ghost` `@rustbot` modify labels: rollup commit 0c0d88864a626f863fd817719984955d13d687b3 Merge: 04a7a559fc8 a6b2d12c921 Author: Jubilee <46493976+workingjubilee@users.noreply.github.com> Date: Tue Apr 2 23:44:29 2024 -0700 Rollup merge of #123388 - tshepang:consistency, r=jhpratt use a consistent style for links commit 04a7a559fc85ca4a2fb5f94980aea2e4c3600309 Merge: f700fb24f39 00f7f57159a Author: Jubilee <46493976+workingjubilee@users.noreply.github.com> Date: Tue Apr 2 23:44:29 2024 -0700 Rollup merge of #123359 - bzEq:aix-libc++abi, r=cuviper Link against libc++abi and libunwind as well when building LLVM wrappers on AIX Unlike `libc++.so` on Linux which is a linker script ```ld INPUT(libc++.so.1 -lc++abi -lunwind) ``` AIX linker doesn't support such script, so `c++abi` and `unwind` have to be specified explicitly. commit f700fb24f39f4b027b03b5b1e4e6723d24f701b8 Merge: abb03935958 ec74a304bb7 Author: Jubilee <46493976+workingjubilee@users.noreply.github.com> Date: Tue Apr 2 23:44:29 2024 -0700 Rollup merge of #123349 - compiler-errors:async-closure-captures, r=oli-obk Fix capture analysis for by-move closure bodies The check we were doing to figure out if a coroutine was borrowing from its parent coroutine-closure was flat-out wrong -- a misunderstanding of mine of the way that `tcx.closure_captures` represents its captures. Fixes #123251 (the miri/ui test I added should more than cover that issue) r? `@oli-obk` -- I recognize that this PR may be underdocumented, so please ask me what I should explain further. commit abb039359588483700ce27bf0ee37cccf636c5fc Merge: 8938f887d3d 5af81873252 Author: Jubilee <46493976+workingjubilee@users.noreply.github.com> Date: Tue Apr 2 23:44:28 2024 -0700 Rollup merge of #122411 - alexcrichton:wasm32-wasip2-cabi-realloc, r=m-ou-se Provide cabi_realloc on wasm32-wasip2 by default This commit provides a component model intrinsic in the standard library by default on the `wasm32-wasip2` target. This intrinsic is not required by the component model itself but is quite common to use, for example it's needed if a wasm module receives a string or a list. The intention of this commit is to provide an overridable definition in the standard library through a weak definition of this function. That means that downstream crates can provide their own customized and more specific versions if they'd like, but the standard library's version should suffice for general-purpose use. commit 76cf07d5df52c07c3cd4cfeea1ab32b1cfba71bf Merge: 8938f887d3d ec313d1edb8 Author: bors Date: Wed Apr 3 06:22:23 2024 +0000 Auto merge of #122225 - DianQK:nits-120268, r=cjgillot Rename `UninhabitedEnumBranching` to `UnreachableEnumBranching` Per [#120268](https://github.com/rust-lang/rust/pull/120268#discussion_r1517492060), I rename `UninhabitedEnumBranching` to `UnreachableEnumBranching` . I solved some nits to add some comments. I adjusted the workaround restrictions. This should be useful for `a <= b` and `if let Some/Ok(v)`. For enum with few variants, `early-tailduplication` should not cause compile time overhead. r? RalfJung commit 120c6b16845c4f70601a0ee1ffe2172f1b0c8c95 Author: Urgau Date: Tue Mar 26 13:30:46 2024 +0100 Add nice header to our README.md commit f6b51c10f3c23a1079d59e01c5a2c2b92955378f Author: Urgau Date: Wed Apr 3 07:59:15 2024 +0200 Add Why Rust? section to the README.md and mention our tools commit 2d47cd77ac7b41a08f5c2ebc22035ed2f39dc076 Author: Zalathar Date: Wed Apr 3 12:55:40 2024 +1100 Check `x86_64` size assertions on `aarch64`, too This makes it easier for contributors on aarch64 workstations (e.g. Macs) to notice when these assertions have been violated. commit 8938f887d3d52327d086511131c70add1ae4773c Merge: b688d53a173 c6348a97683 Author: bors Date: Wed Apr 3 04:14:41 2024 +0000 Auto merge of #123398 - weihanglo:update-cargo, r=weihanglo Update cargo 8 commits in a59aba136aab5510c16b0750a36cbd9916f91796..0637083df5bbdcc951845f0d2eff6999cdb6d30a 2024-03-28 21:21:41 +0000 to 2024-04-02 23:55:05 +0000 - chore(deps): update compatible (rust-lang/cargo#13674) - Maintain sorting of dependency features (rust-lang/cargo#13682) - Update `der` crate (rust-lang/cargo#13692) - fix: bash completion fallback in `nounset` mode (rust-lang/cargo#13686) - CI: Update macos images to macos-13 (rust-lang/cargo#13685) - chore(deps): update rust crate opener to 0.7.0 (rust-lang/cargo#13679) - Remove useless parameters (rust-lang/cargo#13678) - chore(deps): update rust crate supports-unicode to v3 (rust-lang/cargo#13680) r? ghost commit b688d53a1736c17e49328a706a90829a9937a91a Merge: 40f743da23d f756095571c Author: bors Date: Wed Apr 3 02:13:07 2024 +0000 Auto merge of #123396 - jhpratt:rollup-oa54mh1, r=jhpratt Rollup of 5 pull requests Successful merges: - #122865 (Split hir ty lowerer's error reporting code in check functions to mod errors.) - #122935 (rename ptr::from_exposed_addr -> ptr::with_exposed_provenance) - #123182 (Avoid expanding to unstable internal method) - #123203 (Add `Context::ext`) - #123380 (Improve bootstrap comments) r? `@ghost` `@rustbot` modify labels: rollup commit c6348a976831958e88ab90afa91eb17f68dc1e6f Author: Weihang Lo Date: Tue Apr 2 21:38:06 2024 -0400 Update cargo commit 382459e0478bfefacd0c1c5a0240bd4bab518d53 Author: Aaron Loyd Date: Fri Mar 29 17:07:38 2024 -0500 Add section to sanitizer doc for `-Zexternal-clangrt` After spending a week looking for answers to how to do the very thing this flag lets me do, it felt appropriate to document it where I would've expected it to be. commit f756095571c949c3c6f3b18f26c58d9cee92651a Merge: 9c1c0bfcb23 a0721036b6f Author: Jacob Pratt Date: Tue Apr 2 20:37:41 2024 -0400 Rollup merge of #123380 - Nilstrieb:bomments, r=clubby789 Improve bootstrap comments Rewrote a comment I found hard to understand, added some more. commit 9c1c0bfcb23d5411b6cb2b11f209fc6a93dbf97a Merge: e41d7e7aaf2 036085dfec3 Author: Jacob Pratt Date: Tue Apr 2 20:37:40 2024 -0400 Rollup merge of #123203 - jkarneges:context-ext, r=Amanieu Add `Context::ext` This change enables `Context` to carry arbitrary extension data via a single `&mut dyn Any` field. ```rust #![feature(context_ext)] impl Context { fn ext(&mut self) -> &mut dyn Any; } impl ContextBuilder { fn ext(self, data: &'a mut dyn Any) -> Self; fn from(cx: &'a mut Context<'_>) -> Self; fn waker(self, waker: &'a Waker) -> Self; } ``` Basic usage: ```rust struct MyExtensionData { executor_name: String, } let mut ext = MyExtensionData { executor_name: "foo".to_string(), }; let mut cx = ContextBuilder::from_waker(&waker).ext(&mut ext).build(); if let Some(ext) = cx.ext().downcast_mut::() { println!("{}", ext.executor_name); } ``` Currently, `Context` only carries a `Waker`, but there is interest in having it carry other kinds of data. Examples include [LocalWaker](https://github.com/rust-lang/rust/issues/118959), [a reactor interface](https://github.com/rust-lang/libs-team/issues/347), and [multiple arbitrary values by type](https://docs.rs/context-rs/latest/context_rs/). There is also a general practice in the ecosystem of sharing data between executors and futures via thread-locals or globals that would arguably be better shared via `Context`, if it were possible. The `ext` field would provide a low friction (to stabilization) solution to enable experimentation. It would enable experimenting with what kinds of data we want to carry as well as with what data structures we may want to use to carry such data. Dedicated fields for specific kinds of data could still be added directly on `Context` when we have sufficient experience or understanding about the problem they are solving, such as with `LocalWaker`. The `ext` field would be for data for which we don't have such experience or understanding, and that could be graduated to dedicated fields once proven. Both the provider and consumer of the extension data must be aware of the concrete type behind the `Any`. This means it is not possible for the field to carry an abstract interface. However, the field can carry a concrete type which in turn carries an interface. There are different ways one can imagine an interface-carrying concrete type to work, hence the benefit of being able to experiment with such data structures. ## Passing interfaces Interfaces can be placed in a concrete type, such as a struct, and then that type can be casted to `Any`. However, one gotcha is `Any` cannot contain non-static references. This means one cannot simply do: ```rust struct Extensions<'a> { interface1: &'a mut dyn Trait1, interface2: &'a mut dyn Trait2, } let mut ext = Extensions { interface1: &mut impl1, interface2: &mut impl2, }; let ext: &mut dyn Any = &mut ext; ``` To work around this without boxing, unsafe code can be used to create a safe projection using accessors. For example: ```rust pub struct Extensions { interface1: *mut dyn Trait1, interface2: *mut dyn Trait2, } impl Extensions { pub fn new<'a>( interface1: &'a mut (dyn Trait1 + 'static), interface2: &'a mut (dyn Trait2 + 'static), scratch: &'a mut MaybeUninit, ) -> &'a mut Self { scratch.write(Self { interface1, interface2, }) } pub fn interface1(&mut self) -> &mut dyn Trait1 { unsafe { self.interface1.as_mut().unwrap() } } pub fn interface2(&mut self) -> &mut dyn Trait2 { unsafe { self.interface2.as_mut().unwrap() } } } let mut scratch = MaybeUninit::uninit(); let ext: &mut Extensions = Extensions::new(&mut impl1, &mut impl2, &mut scratch); // ext can now be casted to `&mut dyn Any` and back, and used safely let ext: &mut dyn Any = ext; ``` ## Context inheritance Sometimes when futures poll other futures they want to provide their own `Waker` which requires creating their own `Context`. Unfortunately, polling sub-futures with a fresh `Context` means any properties on the original `Context` won't get propagated along to the sub-futures. To help with this, some additional methods are added to `ContextBuilder`. Here's how to derive a new `Context` from another, overriding only the `Waker`: ```rust let mut cx = ContextBuilder::from(parent_cx).waker(&new_waker).build(); ``` commit e41d7e7aaf255d5e14f6afb8fa235e44bff1b975 Merge: e9ef8e1efa3 0fcdf348614 Author: Jacob Pratt Date: Tue Apr 2 20:37:40 2024 -0400 Rollup merge of #123182 - jhpratt:fix-decodable-derive, r=davidtwco Avoid expanding to unstable internal method Fixes #123156 Rather than expanding to `std::rt::begin_panic`, the expansion is now to `unreachable!()`. The resulting behavior is identical. A test that previously triggered the same error as #123156 has been added to ensure it does not regress. r? compiler commit 7a1a4565f2c6b17b7604c10b4c013823ae93d18c Merge: 79a1bddaf3f 905550149f5 Author: Jacob Pratt Date: Tue Apr 2 20:37:39 2024 -0400 Rollup merge of #122935 - RalfJung:with-exposed-provenance, r=Amanieu rename ptr::from_exposed_addr -> ptr::with_exposed_provenance As discussed on [Zulip](https://rust-lang.zulipchat.com/#narrow/stream/136281-t-opsem/topic/To.20expose.20or.20not.20to.20expose/near/427757066). The old name, `from_exposed_addr`, makes little sense as it's not the address that is exposed, it's the provenance. (`ptr.expose_addr()` stays unchanged as we haven't found a better option yet. The intended interpretation is "expose the provenance and return the address".) The new name nicely matches `ptr::without_provenance`. commit e530b3d19c91e3e061828aabe5aababef5fc2717 Merge: 11b28d44bd4 4d623015e0c Author: Jacob Pratt Date: Tue Apr 2 20:37:39 2024 -0400 Rollup merge of #122935 - RalfJung:with-exposed-provenance, r=Amanieu rename ptr::from_exposed_addr -> ptr::with_exposed_provenance As discussed on [Zulip](https://rust-lang.zulipchat.com/#narrow/stream/136281-t-opsem/topic/To.20expose.20or.20not.20to.20expose/near/427757066). The old name, `from_exposed_addr`, makes little sense as it's not the address that is exposed, it's the provenance. (`ptr.expose_addr()` stays unchanged as we haven't found a better option yet. The intended interpretation is "expose the provenance and return the address".) The new name nicely matches `ptr::without_provenance`. commit e9ef8e1efa387fa235d63c191492f63f70959348 Merge: 0697ee9af5a f2cff5ebb9e Author: Jacob Pratt Date: Tue Apr 2 20:37:39 2024 -0400 Rollup merge of #122935 - RalfJung:with-exposed-provenance, r=Amanieu rename ptr::from_exposed_addr -> ptr::with_exposed_provenance As discussed on [Zulip](https://rust-lang.zulipchat.com/#narrow/stream/136281-t-opsem/topic/To.20expose.20or.20not.20to.20expose/near/427757066). The old name, `from_exposed_addr`, makes little sense as it's not the address that is exposed, it's the provenance. (`ptr.expose_addr()` stays unchanged as we haven't found a better option yet. The intended interpretation is "expose the provenance and return the address".) The new name nicely matches `ptr::without_provenance`. commit 0697ee9af5a0493631606e5fbe80cc802a95eb23 Merge: 88c2f4f5f50 1012218ba8e Author: Jacob Pratt Date: Tue Apr 2 20:37:39 2024 -0400 Rollup merge of #122865 - surechen:refactor_astconv_error_report_20240321, r=lcnr Split hir ty lowerer's error reporting code in check functions to mod errors. Move some error report codes to mod `astconv/errors.rs` r? `@lcnr` commit 40f743da23d869c57556bca473c0d360f8528f94 Merge: 88c2f4f5f50 56dbeeb5ac0 Author: bors Date: Wed Apr 3 00:09:44 2024 +0000 Auto merge of #122791 - compiler-errors:make-coinductive-always, r=lcnr Make inductive cycles always ambiguous This makes inductive cycles always result in ambiguity rather than be treated like a stack-dependent error. This has some interactions with specialization, and so breaks a few UI tests that I don't agree should've ever worked in the first place, and also breaks a handful of crates in a way that I don't believe is a problem. On the bright side, it puts us in a better spot when it comes to eventually enabling coinduction everywhere. ## Results This was cratered in https://github.com/rust-lang/rust/pull/116494#issuecomment-2008657494, which boils down to two regressions: * `lu_packets` - This code should have never compiled in the first place. More below. * **ALL** other regressions are due to `commit_verify@0.11.0-beta.1` (edit: and `commit_verify@0.10.x`) - This actually seems to be fixed in version `0.11.0-beta.5`, which is the *most* up to date version, but it's still prerelease on crates.io so I don't think cargo ends up picking `beta.5` when building dependent crates. ### `lu_packets` Firstly, this crate uses specialization, so I think it's automatically worth breaking. However, I've minimized [the regression](https://crater-reports.s3.amazonaws.com/pr-116494-3/try%23d614ed876e31a5f3ad1d0fbf848fcdab3a29d1d8/gh/lcdr.lu_packets/log.txt) to: ```rust // Upstream crate pub trait Serialize {} impl Serialize for &() {} impl Serialize for &[S] where for<'a> &'a S: Serialize {} // ----------------------------------------------------------------------- // // Downstream crate #![feature(specialization)] #![allow(incomplete_features, unused)] use upstream::Serialize; trait Replica { fn serialize(); } impl Replica for T { default fn serialize() {} } impl Replica for Option where for<'a> &'a T: Serialize, { fn serialize() {} } ``` Specifically this fails when computing the specialization graph for the `downstream` crate. The code ends up cycling on `&[?0]: Serialize` when we equate `&?0 = &[?1]` during impl matching, which ends up needing to prove `&[?1]: Serialize`, which since cycles are treated like ambiguity, ends up in a **fatal overflow**. For some reason this requires two crates, squashing them into one crate doesn't work. Side-note: This code is subtly order dependent. When minimizing, I ended up having the code start failing on `nightly` very easily after removing and reordering impls. This seems to me all the more reason to remove this behavior altogether. ## Side-note: Item Bounds (edit: this was fixed independently in #121123) Due to the changes in #120584 where we now consider an alias's item bounds *and* all the item bounds of the alias's nested self type aliases, I've had to add e6b64c61941120f734657106ae2479d05b463197 which is a hack to make sure we're not eagerly normalizing bounds that have nothing to do with the predicate we're trying to solve, and which result in. This is fixed in a more principled way in #121123. --- r? lcnr for an initial review commit ec74a304bb737f24d6e7f2fb8c3a2b3cf3575f0f Author: Michael Goulet Date: Tue Apr 2 11:26:57 2024 -0400 Comments, comments, comments commit a1a1f41027c16a940c1de9e445064692110637c8 Author: Michael Goulet Date: Mon Apr 1 14:42:00 2024 -0400 Fix capture analysis for by-move closure bodies commit 4cb5643bd4b9a66f8c8b8fee2505a7f09b8ed10a Author: Michael Goulet Date: Tue Apr 2 19:40:12 2024 -0400 Fix contains_exterior_struct_lit commit ab821aed0c048964a86285d534ca93203d61af89 Author: Michael Goulet Date: Tue Apr 2 19:30:32 2024 -0400 Fix precedence of postfix match commit bed8b70d674de72e0e66ab022a64c33f0b1df15e Author: Michael Goulet Date: Tue Apr 2 19:06:28 2024 -0400 Fix suggestions for match non-exhaustiveness commit 8289dadfbc5af6af9b7995e3d41b7cbda3a1139c Author: Zalathar Date: Tue Feb 13 23:00:49 2024 +1100 coverage: Correctly report and check LLVM's coverage mapping version commit 036085dfec3f58cf63f8014f6c94adddace3b527 Author: Justin Karneges Date: Tue Apr 2 15:45:53 2024 -0700 set tracking issue commit 9d116e8e188a84d97e1b64f54410fda7ec6f2dc6 Author: Michael Goulet Date: Tue Apr 2 18:31:42 2024 -0400 Don't ICE for postfix match after as commit 0fcdf3486146b5b2fc5d4b7f3bcaffbbb6a95fdc Author: Jacob Pratt Date: Fri Mar 29 05:35:45 2024 +0000 Avoid expanding to unstable internal method commit 88c2f4f5f50ace5ddc7655ea311435104d3659bd Merge: a77322c16f1 31900b4c13d Author: bors Date: Tue Apr 2 21:23:53 2024 +0000 Auto merge of #123385 - matthiaskrgr:rollup-v69vjbn, r=matthiaskrgr Rollup of 8 pull requests Successful merges: - #123198 (Add fn const BuildHasherDefault::new) - #123226 (De-LLVM the unchecked shifts [MCP#693]) - #123302 (Make sure to insert `Sized` bound first into clauses list) - #123348 (rustdoc: add a couple of regression tests) - #123362 (Check that nested statics in thread locals are duplicated per thread.) - #123368 (CFI: Support non-general coroutines) - #123375 (rustdoc: synthetic auto trait impls: accept unresolved region vars for now) - #123378 (Update sysinfo to 0.30.8) Failed merges: - #123349 (Fix capture analysis for by-move closure bodies) r? `@ghost` `@rustbot` modify labels: rollup commit 049a91753594422b6c672bf5a46e04076fc45d9e Author: Trevor Gross Date: Tue Apr 2 16:19:55 2024 -0400 Put basic impls for f16 and f128 behind cfg(not(bootstrap)) We will lose `f16` and `f128` in the beta compiler after the revert for lands. Change what was added in to be behind `#[cfg(not(bootstrap))]` to account for this. commit e457b77e2a9673db5e4e83f79d8961b6e2cb454b Author: Chris Denton Date: Tue Apr 2 19:37:21 2024 +0000 Avoid panicking unnecessarily on startup commit a6b2d12c921552082d6849c75531da666a9c9c82 Author: Tshepang Mbambo Date: Tue Apr 2 21:41:16 2024 +0200 use a consistent style for links commit 31900b4c13d733ec3ea4aa0162fdc1da974accc1 Merge: 8e271d70a24 b626f013102 Author: Matthias Krüger Date: Tue Apr 2 21:22:04 2024 +0200 Rollup merge of #123378 - ferrocene:hoverbear/bump-sysinfo-to-0.30.8, r=Nilstrieb Update sysinfo to 0.30.8 Fixes a Mac specific issue when using `metrics = true` in `config.toml`. ```config.toml # Collect information and statistics about the current build and writes it to # disk. Enabling this or not has no impact on the resulting build output. The # schema of the file generated by the build metrics feature is unstable, and # this is not intended to be used during local development. metrics = true ``` During repeated builds, as the generated `metrics.json` grew, eventually `refresh_cpu()` would be called in quick enough succession (specifically: under 200ms) that a divide by zero would occur, leading to a `NaN` which would not be serialized, then when the `metrics.json` was re-read it would fail to parse. That error looks like this (collected from Ferrocene's CI): ``` Compiling rustdoc-tool v0.0.0 (/Users/distiller/project/src/tools/rustdoc) Finished release [optimized] target(s) in 38.37s thread 'main' panicked at src/utils/metrics.rs:180:21: serde_json::from_slice::(&contents) failed with invalid type: null, expected f64 at line 1 column 9598 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace Build completed unsuccessfully in 0:00:40 Exited with code exit status 1 ``` Related: https://github.com/GuillaumeGomez/sysinfo/pull/1236 commit 8e271d70a249b69a1dbb4667e106dd9cd9d94d1a Merge: 93729488895 70b4ace09d5 Author: Matthias Krüger Date: Tue Apr 2 21:22:04 2024 +0200 Rollup merge of #123375 - fmease:rustdoc-sati-re-hotfix, r=GuillaumeGomez rustdoc: synthetic auto trait impls: accept unresolved region vars for now https://github.com/rust-lang/rust/pull/123348#issuecomment-2032494255: > Right, [in #123340] I've intentionally changed a `vid_map.get(vid).unwrap_or(r)` to a `vid_map[vid]` making rustdoc panic if `rustc::AutoTraitFinder` returns a region inference variable that cannot be resolved because that is really fishy. I can change it back with a `FIXME: investigate` […]. [O]nce I [fully] understand [the arcane] `rustc::AutoTraitFinder` [I] can fix the underlying issue if there's one. > > `rustc::AutoTraitFinder` can also return placeholder regions `RePlaceholder` which doesn't seem right either and which makes rustdoc ICE, too (we have a GitHub issue for that already[, namely #120606]). Fixes #123370. Fixes #112242. r? ``@GuillaumeGomez`` commit 93729488895537f7ddc0dccb1ec817eb1d8a28af Merge: 5b717684ffc a333b82d04c Author: Matthias Krüger Date: Tue Apr 2 21:22:03 2024 +0200 Rollup merge of #123368 - maurer:cfi-non-general-coroutines, r=compiler-errors CFI: Support non-general coroutines Previously, we assumed all `ty::Coroutine` were general coroutines and attempted to generalize them through the `Coroutine` trait. Select appropriate traits for each kind of coroutine. I have this marked as a draft because it currently only fixes async coroutines, and I think it make sense to try to fix gen/async gen coroutines before this is merged. If the issue [mentioned](https://github.com/rust-lang/rust/pull/123106#issuecomment-2030794213) in the original PR is actually affecting someone, we can land this as is to remedy it. commit 5b717684ffc5f908a9a3953a21cd3d3d20020c1e Merge: 464f2643879 64b75f736dd Author: Matthias Krüger Date: Tue Apr 2 21:22:03 2024 +0200 Rollup merge of #123362 - oli-obk:thread_local_nested_statics, r=estebank Check that nested statics in thread locals are duplicated per thread. follow-up to #123310 cc ``@compiler-errors`` ``@RalfJung`` fwiw: I have no idea how thread local statics make that work under LLVM, and miri fails on this example, which I would have expected to be the correct behaviour. Since the `#[thread_local]` attribute is just an internal implementation detail, I'm just going to start hard erroring on nested mutable statics in thread locals. commit 464f264387978e4d6b8df96791bbfc76fcd10d98 Merge: a38dde92896 5ee4d13709c Author: Matthias Krüger Date: Tue Apr 2 21:22:02 2024 +0200 Rollup merge of #123348 - fmease:add-synth-auto-trait-impls-tests, r=GuillaumeGomez rustdoc: add a couple of regression tests Fixes #114657. Fixes #112828. Fixes #107715. r? rustdoc commit a38dde928964a5a6b1b36ae7c45f48f8966762cb Merge: 1b0e46f8a05 f2fd2d8c708 Author: Matthias Krüger Date: Tue Apr 2 21:22:01 2024 +0200 Rollup merge of #123302 - compiler-errors:sized-bound-first, r=estebank Make sure to insert `Sized` bound first into clauses list #120323 made it so that we don't insert an implicit `Sized` bound whenever we see an *explicit* `Sized` bound. However, since the code that inserts implicit sized bounds puts the bound as the *first* in the list, that means that it had the **side-effect** of possibly meaning we check `Sized` *after* checking other trait bounds. If those trait bounds result in ambiguity or overflow or something, it may change how we winnow candidates. (**edit: SEE** #123303) This is likely the cause for the regression in https://github.com/rust-lang/rust/issues/123279#issuecomment-2028899598, since the impl... ```rust impl AsJob for T { // <----- changing this to `Sized + Job` or just `Job` (which turns into `Sized + Job`) will FIX the issue. } ``` ...looks incredibly suspicious. Fixes [after beta-backport] #123279. Alternative is to revert #120323. I don't have a strong opinion about this, but think it may be nice to keep the diagnostic changes around. commit 1b0e46f8a05de7497ea17c8ad0a1c525c1b123f6 Merge: d63ddef8033 46265218319 Author: Matthias Krüger Date: Tue Apr 2 21:22:01 2024 +0200 Rollup merge of #123226 - scottmcm:u32-shifts, r=WaffleLapkin De-LLVM the unchecked shifts [MCP#693] This is just one part of the MCP (https://github.com/rust-lang/compiler-team/issues/693), but it's the one that IMHO removes the most noise from the standard library code. Seems net simpler this way, since MIR already supported heterogeneous shifts anyway, and thus it's not more work for backends than before. r? WaffleLapkin commit d63ddef8033b056b73efc4531769dca07e3a2f66 Merge: 029cb1b13b6 54ab4258397 Author: Matthias Krüger Date: Tue Apr 2 21:22:00 2024 +0200 Rollup merge of #123198 - krtab:build_hasher_default_const_new, r=Amanieu Add fn const BuildHasherDefault::new See [tracking issue](https://github.com/rust-lang/rust/issues/123197) for justification. commit 79a1bddaf3f6d7727239749178eb26ac1ec2e288 Merge: b2f6349b49d 629a772585f Author: bors Date: Tue Apr 2 19:21:44 2024 +0000 Auto merge of #118310 - scottmcm:three-way-compare, r=davidtwco Add `Ord::cmp` for primitives as a `BinOp` in MIR Update: most of this OP was written months ago. See https://github.com/rust-lang/rust/pull/118310#issuecomment-2016940014 below for where we got to recently that made it ready for review. --- There are dozens of reasonable ways to implement `Ord::cmp` for integers using comparison, bit-ops, and branches. Those differences are irrelevant at the rust level, however, so we can make things better by adding `BinOp::Cmp` at the MIR level: 1. Exactly how to implement it is left up to the backends, so LLVM can use whatever pattern its optimizer best recognizes and cranelift can use whichever pattern codegens the fastest. 2. By not inlining those details for every use of `cmp`, we drastically reduce the amount of MIR generated for `derive`d `PartialOrd`, while also making it more amenable to MIR-level optimizations. Having extremely careful `if` ordering to μoptimize resource usage on broadwell (#63767) is great, but it really feels to me like libcore is the wrong place to put that logic. Similarly, using subtraction [tricks](https://graphics.stanford.edu/~seander/bithacks.html#CopyIntegerSign) (#105840) is arguably even nicer, but depends on the optimizer understanding it (https://github.com/llvm/llvm-project/issues/73417) to be practical. Or maybe [bitor is better than add](https://discourse.llvm.org/t/representing-in-ir/67369/2?u=scottmcm)? But maybe only on a future version that [has `or disjoint` support](https://discourse.llvm.org/t/rfc-add-or-disjoint-flag/75036?u=scottmcm)? And just because one of those forms happens to be good for LLVM, there's no guarantee that it'd be the same form that GCC or Cranelift would rather see -- especially given their very different optimizers. Not to mention that if LLVM gets a spaceship intrinsic -- [which it should](https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Suboptimal.20inlining.20in.20std.20function.20.60binary_search.60/near/404250586) -- we'll need at least a rustc intrinsic to be able to call it. As for simplifying it in Rust, we now regularly inline `{integer}::partial_cmp`, but it's quite a large amount of IR. The best way to see that is with https://github.com/rust-lang/rust/commit/8811efa88b25b5e41d63850e6047e8257c677858#diff-d134c32d028fbe2bf835fef2df9aca9d13332dd82284ff21ee7ebf717bfa4765R113 -- I added a new pre-codegen MIR test for a simple 3-tuple struct, and this PR change it from 36 locals and 26 basic blocks down to 24 locals and 8 basic blocks. Even better, as soon as the construct-`Some`-then-match-it-in-same-BB noise is cleaned up, this'll expose the `Cmp == 0` branches clearly in MIR, so that an InstCombine (#105808) can simplify that to just a `BinOp::Eq` and thus fix some of our generated code perf issues. (Tracking that through today's `if a < b { Less } else if a == b { Equal } else { Greater }` would be *much* harder.) --- r? `@ghost` But first I should check that perf is ok with this ~~...and my true nemesis, tidy.~~ commit a77322c16f188402fa22a5e87100acce42433cbc Merge: 029cb1b13b6 c59e93c753b Author: bors Date: Tue Apr 2 19:21:44 2024 +0000 Auto merge of #118310 - scottmcm:three-way-compare, r=davidtwco Add `Ord::cmp` for primitives as a `BinOp` in MIR Update: most of this OP was written months ago. See https://github.com/rust-lang/rust/pull/118310#issuecomment-2016940014 below for where we got to recently that made it ready for review. --- There are dozens of reasonable ways to implement `Ord::cmp` for integers using comparison, bit-ops, and branches. Those differences are irrelevant at the rust level, however, so we can make things better by adding `BinOp::Cmp` at the MIR level: 1. Exactly how to implement it is left up to the backends, so LLVM can use whatever pattern its optimizer best recognizes and cranelift can use whichever pattern codegens the fastest. 2. By not inlining those details for every use of `cmp`, we drastically reduce the amount of MIR generated for `derive`d `PartialOrd`, while also making it more amenable to MIR-level optimizations. Having extremely careful `if` ordering to μoptimize resource usage on broadwell (#63767) is great, but it really feels to me like libcore is the wrong place to put that logic. Similarly, using subtraction [tricks](https://graphics.stanford.edu/~seander/bithacks.html#CopyIntegerSign) (#105840) is arguably even nicer, but depends on the optimizer understanding it (https://github.com/llvm/llvm-project/issues/73417) to be practical. Or maybe [bitor is better than add](https://discourse.llvm.org/t/representing-in-ir/67369/2?u=scottmcm)? But maybe only on a future version that [has `or disjoint` support](https://discourse.llvm.org/t/rfc-add-or-disjoint-flag/75036?u=scottmcm)? And just because one of those forms happens to be good for LLVM, there's no guarantee that it'd be the same form that GCC or Cranelift would rather see -- especially given their very different optimizers. Not to mention that if LLVM gets a spaceship intrinsic -- [which it should](https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Suboptimal.20inlining.20in.20std.20function.20.60binary_search.60/near/404250586) -- we'll need at least a rustc intrinsic to be able to call it. As for simplifying it in Rust, we now regularly inline `{integer}::partial_cmp`, but it's quite a large amount of IR. The best way to see that is with https://github.com/rust-lang/rust/commit/8811efa88b25b5e41d63850e6047e8257c677858#diff-d134c32d028fbe2bf835fef2df9aca9d13332dd82284ff21ee7ebf717bfa4765R113 -- I added a new pre-codegen MIR test for a simple 3-tuple struct, and this PR change it from 36 locals and 26 basic blocks down to 24 locals and 8 basic blocks. Even better, as soon as the construct-`Some`-then-match-it-in-same-BB noise is cleaned up, this'll expose the `Cmp == 0` branches clearly in MIR, so that an InstCombine (#105808) can simplify that to just a `BinOp::Eq` and thus fix some of our generated code perf issues. (Tracking that through today's `if a < b { Less } else if a == b { Equal } else { Greater }` would be *much* harder.) --- r? `@ghost` But first I should check that perf is ok with this ~~...and my true nemesis, tidy.~~ commit 473a70de8457645df7a49558d6ba27405f966ee0 Author: Matthew Maurer Date: Mon Mar 25 17:23:55 2024 +0000 CFI: Support function pointers for trait methods Adds support for both CFI and KCFI for attaching concrete and abstract types to functions. KCFI does this through generation of `ReifyShim` on any function pointer that could go in a vtable, and checking the `ReifyReason` when emitting the instance. CFI does this by attaching both the concrete and abstract type to every instance. TypeID codegen tests are switched to be anchored on the left rather than the right in order to allow emission of additional type attachments. Fixes #115953 commit 6aa89f684e4427a9d08e35b572f9071705105140 Author: Matthew Maurer Date: Mon Mar 25 19:27:43 2024 +0000 Track reason for creating a `ReifyShim` KCFI needs to be able to tell which kind of `ReifyShim` it is examining in order to decide whether to use a concrete type (`FnPtr` case) or an abstract case (`Vtable` case). You can *almost* tell this from context, but there is one case where you can't - if a trait has a method which is *not* `#[track_caller]`, with an impl that *is* `#[track_caller]`, both the vtable and a function pointer created from that method will be `ReifyShim(def_id)`. Currently, the reason is optional to ensure no additional unique `ReifyShim`s are added without KCFI on. However, the case in which an extra `ReifyShim` is created is sufficiently rare that this may be worth revisiting to reduce complexity. commit a0721036b6fdfac5c239fc2f1f291ab70dd91aff Author: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Tue Apr 2 20:36:48 2024 +0200 Improve bootstrap comments Rewrote a comment I found hard to understand, added some more. commit 93c2bace58b36ba297f06505b55aef5b8eba954f Author: Matthew Maurer Date: Tue Apr 2 18:23:28 2024 +0000 CFI: Switch sense of type erasure flag Previously, we had `NO_SELF_TYPE_ERASURE`, a negative configuration. Now we have `ERASE_SELF_TYPE`, a positive configuration. commit cc5105d246c77d54cabb4f595d4e87c14358073d Author: Michael Goulet Date: Tue Apr 2 14:13:25 2024 -0400 Don't create an FnDef of a DefKind::Variant, use the ctor def id commit b4e2d75d3598a4f9e89d918255e84053c68a4583 Author: Michael Goulet Date: Tue Apr 2 14:12:40 2024 -0400 Assert FnDef only constructed with functions (or fn-like ctors) commit b626f0131027c0b352274316d34f1f61df78faa3 Author: Ana Hobden Date: Tue Apr 2 10:44:23 2024 -0700 Update sysinfo to 0.30.8 Fixes a Mac specific issue when using `build-metrics = true` in `config.toml` commit a333b82d04c6077d639c664d45d051a0bdcbabed Author: Matthew Maurer Date: Tue Apr 2 15:17:33 2024 +0000 CFI: Support non-general coroutines Previously, we assumed all `ty::Coroutine` were general coroutines and attempted to generalize them through the `Coroutine` trait. Select appropriate traits for each kind of coroutine. commit 46265218319573cb9afcce0c2e8dc7411ab28768 Author: scottmcm Date: Tue Apr 2 17:21:20 2024 +0000 Update tests/mir-opt/inline/unchecked_shifts.rs Co-authored-by: Waffle Maybe commit 327aa199dd17d31db2f3d8ed80d15a394101d6b5 Author: Scott McMurray Date: Tue Apr 2 10:17:21 2024 -0700 Improve the `build_shift_expr_rhs` comment commit 029cb1b13b6388b95e64e8996ec8b41a9f3cf16e Merge: 36b6f9b58e7 44680680efb Author: bors Date: Tue Apr 2 17:08:11 2024 +0000 Auto merge of #123372 - GuillaumeGomez:rollup-nwxdzev, r=GuillaumeGomez Rollup of 4 pull requests Successful merges: - #122614 (rustdoc-search: shard the search result descriptions) - #123338 (Update to new browser-ui-test version) - #123366 (Minor by_move_body impl cleanups) - #123371 (Remove dangling `.mir.stderr` and `.thir.stderr` test files) r? `@ghost` `@rustbot` modify labels: rollup commit 70b4ace09d5b1033c751654b9bbe714eeb4d5b4d Author: León Orell Valerian Liehr Date: Tue Apr 2 18:59:17 2024 +0200 rustdoc: synthetic auto trait impls: accept unresolved region vars for now commit 5ee4d13709c6de71c206529563e2736cd6ac0033 Author: León Orell Valerian Liehr Date: Tue Apr 2 18:37:01 2024 +0200 rustdoc: add a couple of regression tests commit 44680680efb5efc3dbfc54619a19015b967271d7 Merge: 0bf8140a5e5 858a1dfd5bc Author: Guillaume Gomez Date: Tue Apr 2 18:18:51 2024 +0200 Rollup merge of #123371 - eduardosm:dangling-test-files, r=compiler-errors Remove dangling `.mir.stderr` and `.thir.stderr` test files They are not needed since https://github.com/rust-lang/rust/pull/117673 commit 0bf8140a5e5960229a5d72718a8fe1bb87ef6055 Merge: c3a124e9d62 6f3cc0903c7 Author: Guillaume Gomez Date: Tue Apr 2 18:18:51 2024 +0200 Rollup merge of #123366 - oli-obk:cleanups_async_closures, r=compiler-errors Minor by_move_body impl cleanups r? `@compiler-errors` commit c3a124e9d626d77d6a0fa3f91b638e163c9d256d Merge: 3aab05eecb5 0bb1ec729f5 Author: Guillaume Gomez Date: Tue Apr 2 18:18:51 2024 +0200 Rollup merge of #123338 - GuillaumeGomez:update-browser-ui-test, r=notriddle Update to new browser-ui-test version This new version brings a lot of new internal improvements (mostly around validating the commands input). It also improved some command names and arguments. r? `@notriddle` commit 3aab05eecb5e629407089e3076b719bca3e7fbf9 Merge: 5dbaafdb930 a272007a209 Author: Guillaume Gomez Date: Tue Apr 2 18:18:50 2024 +0200 Rollup merge of #122614 - notriddle:notriddle/search-desc, r=GuillaumeGomez rustdoc-search: shard the search result descriptions ## Preview This makes no visual changes to rustdoc search. It's a pure perf improvement.
old Preview: WebPageTest Comparison with before branch on a sort of worst case (searching `vec`, winds up downloading most of the shards anyway): Waterfall diagram: ![image](https://github.com/rust-lang/rust/assets/1593513/39548f0c-7ad6-411b-abf8-f6668ff4da18)
Preview: WebPageTest Comparison with before branch on a sort of worst case (searching `vec`, winds up downloading most of the shards anyway): ![image](https://github.com/rust-lang/rust/assets/1593513/4be1f9ff-c3ff-4b96-8f5b-b264df2e662d) ## Description r? `@GuillaumeGomez` The descriptions are, on almost all crates[^1], the majority of the size of the search index, even though they aren't really used for searching. This makes it relatively easy to separate them into their own files. Additionally, this PR pulls out information about whether there's a description into a bitmap. This allows us to sort, truncate, *then* download. This PR also bumps us to ES8. Out of the browsers we support, all of them support async functions according to caniuse. https://caniuse.com/async-functions [^1]: , a crate with 44MiB of pure names and no descriptions for them, is an outlier and should not be counted. But this PR should improve it, by replacing a long line of empty strings with a compressed bitmap with a single Run section. Just not very much. ## Detailed sizes ```console $ cat test.sh set -ex cp ../search-index*.js search-index.js awk 'FNR==NR {a++;next} FNR1 {gsub(/\],\\$/,""); gsub(/^\["[^"]+",/,""); print} {next}' | sed -E "s:\\\\':':g" > search-index.json jq -c '.t' search-index.json > t.json jq -c '.n' search-index.json > n.json jq -c '.q' search-index.json > q.json jq -c '.D' search-index.json > D.json jq -c '.e' search-index.json > e.json jq -c '.i' search-index.json > i.json jq -c '.f' search-index.json > f.json jq -c '.c' search-index.json > c.json jq -c '.p' search-index.json > p.json jq -c '.a' search-index.json > a.json du -hs t.json n.json q.json D.json e.json i.json f.json c.json p.json a.json $ bash test.sh + cp ../search-index1.78.0.js search-index.js + awk 'FNR==NR {a++;next} FNR1 {gsub(/\],\\$/,""); gsub(/^\["[^"]+",/,""); print} {next}' + sed -E 's:\\'\'':'\'':g' + jq -c .t search-index.json + jq -c .n search-index.json + jq -c .q search-index.json + jq -c .D search-index.json + jq -c .e search-index.json + jq -c .i search-index.json + jq -c .f search-index.json + jq -c .c search-index.json + jq -c .p search-index.json + jq -c .a search-index.json + du -hs t.json n.json q.json D.json e.json i.json f.json c.json p.json a.json 64K t.json 800K n.json 8.0K q.json 4.0K D.json 16K e.json 192K i.json 544K f.json 4.0K c.json 36K p.json 20K a.json ``` These are, roughly, the size of each section in the standard library (this tool actually excludes libtest, for parsing-json-with-awk reasons, but libtest is tiny so it's probably not important). t = item type, like "struct", "free fn", or "type alias". Since one byte is used for every item, this implies that there are approximately 64 thousand items in the standard library. n = name, and that's now the largest section of the search index with the descriptions removed from it q = parent *module* path, stored parallel to the items within D = the size of each description shard, stored as vlq hex numbers e = empty description bit flags, stored as a roaring bitmap i = parent *type* index as a link into `p`, stored as decimal json numbers; used only for associated types; might want to switch to vlq hex, since that's shorter, but that would be a separate pr f = function signature, stored as lists of lists that index into `p` c = deprecation flag, stored as a roaring bitmap p = parent *type*, stored separately and linked into from `i` and `f` a = alias, as [[key, value]] pairs ## Search performance http://notriddle.com/rustdoc-html-demo-11/perf-shard/index.html For example, in stm32f4:
beforeafter
``` Testing T -> U ... in_args = 0, returned = 0, others = 200 wall time = 617 Testing T, U ... in_args = 0, returned = 0, others = 200 wall time = 198 Testing T -> T ... in_args = 0, returned = 0, others = 200 wall time = 282 Testing crc32 ... in_args = 0, returned = 0, others = 0 wall time = 426 Testing spi::pac ... in_args = 0, returned = 0, others = 0 wall time = 673 ``` ``` Testing T -> U ... in_args = 0, returned = 0, others = 200 wall time = 716 Testing T, U ... in_args = 0, returned = 0, others = 200 wall time = 207 Testing T -> T ... in_args = 0, returned = 0, others = 200 wall time = 289 Testing crc32 ... in_args = 0, returned = 0, others = 0 wall time = 418 Testing spi::pac ... in_args = 0, returned = 0, others = 0 wall time = 687 ```
``` user: 005.345 s sys: 002.955 s wall: 006.899 s child_RSS_high: 583664 KiB group_mem_high: 557876 KiB ``` ``` user: 004.652 s sys: 000.565 s wall: 003.865 s child_RSS_high: 538696 KiB group_mem_high: 511724 KiB ```
This perf tester is janky and unscientific enough that the apparent differences might just be noise. If it's not an order of magnitude, it's probably not real. ## Future possibilities * Currently, results are not shown until the descriptions are downloaded. Theoretically, the description-less results could be shown. But actually doing that, and making sure it works properly, would require extra work (we have to be careful to avoid layout jumps). * More than just descriptions can be sharded this way. But we have to be careful to make sure the size wins are worth the round trips. Ideally, data that’s needed only for display should be sharded while data needed for search isn’t. * [Full text search](https://internals.rust-lang.org/t/full-text-search-for-rustdoc-and-doc-rs/20427) also needs this kind of infrastructure. A good implementation might store a compressed bloom filter in the search index, then download the full keyword in shards. But, we have to be careful not just of the amount readers have to download, but also of the amount that [publishers](https://gist.github.com/notriddle/c289e77f3ed469d1c0238d1d135d49e1) have to store. commit 858a1dfd5bc7c27655d07d45c792fccb4ac0c43d Author: Eduardo Sánchez Muñoz Date: Tue Apr 2 18:02:06 2024 +0200 Remove dangling `.mir.stderr` and `.thir.stderr` test files commit 9d200f2d88600c685e4d2945308b5116c29849e7 Author: Jules Bertholet Date: Tue Apr 2 10:54:29 2024 -0500 Address review comments commit 36b6f9b58e78a1225a322a759e42c262e6dc8d5d Merge: 5dbaafdb930 bb439900dda Author: bors Date: Tue Apr 2 15:06:37 2024 +0000 Auto merge of #123354 - SteveLauC:fix/haiku, r=Nilstrieb fix: build on haiku by adding missing import Fix the build on Haiku by adding a missing import ``` error[E0433]: failed to resolve: use of undeclared crate or module `slice` --> /localhome/somers/.rustup/toolchains/nightly-x86_64-unknown-freebsd/lib/rustlib/src/rust/library/std/src/sys/pal/unix/thread.rs:272:24 | 272 | let name = slice::from_raw_parts(info.name.as_ptr() as *const u8, info.name.len()); | ^^^^^ use of undeclared crate or module `slice` | help: consider importing one of these items | 1 + use alloc::slice; | 1 + use core::slice; | 1 + use crate::slice; ``` Closes #123343 commit a272007a209a4327ad8122af1ad9f9620f04724e Author: Michael Howell Date: Tue Apr 2 07:55:52 2024 -0700 Clean up src/librustdoc/html/render/search_index/encode.rs Co-authored-by: Guillaume Gomez commit 6f3cc0903c7d540acc10ea13fa2cf6aaec8f64cd Author: Oli Scherer Date: Tue Apr 2 14:13:05 2024 +0000 Avoid an `is_empty()` followed by an index op in favor of a single fallible op commit b4993c47f2f1348356d87c92ea54fd20fcd71cb7 Author: Oli Scherer Date: Tue Apr 2 14:10:06 2024 +0000 Prefer `UnordSet` over `FxHashSet` where possible commit f9f854f4283c9239769f807f14a7475256c8730d Merge: 3b1b654f565 e575f05a8b6 Author: bors Date: Tue Apr 2 13:40:00 2024 +0000 Auto merge of #12617 - y21:issue-12616, r=Alexendoo avoid an ICE in `ptr_as_ptr` when getting the def_id of a local Fixes #12616 `Res::def_id` can panic, so avoid calling it in favor of `opt_def_id`, so we can gracefully handle resolutions that don't have a `DefId` (e.g. local variables) and get a false negative in the worst case, rather than an ICE changelog: Fix ICE in [`ptr_as_ptr`] when the cast expression is a function call to a local variable commit 0478d26c8b32acb56df3d89cb21847974ff0f312 Author: not-elm Date: Tue Apr 2 22:24:17 2024 +0900 FIX(12334): manual_swap auto fix Initialization expressions are now generated when index is bound to a variable. FIX: Check to see if variables are used after swap FIX: rename StmtKind::Local to StmtKind::Let commit e575f05a8b6895fdafd27f5fccca54b60876f318 Author: y21 <30553356+y21@users.noreply.github.com> Date: Tue Apr 2 15:04:25 2024 +0200 avoid an ICE in `ptr_as_ptr` when getting the def_id of a local commit 64b75f736ddaf10887a508941ab7dea9a67d01d4 Author: Oli Scherer Date: Tue Apr 2 13:00:46 2024 +0000 Forbid implicit nested statics in thread local statics commit 5dbaafdb9305df5332157e74eaaa55c615aa489f Merge: 0e682e98754 069e7f2a768 Author: bors Date: Tue Apr 2 12:13:44 2024 +0000 Auto merge of #123340 - fmease:rustdoc-simplify-auto-trait-impl-synth, r=GuillaumeGomez rustdoc: heavily simplify the synthesis of auto trait impls `gd --numstat HEAD~2 HEAD src/librustdoc/clean/auto_trait.rs` **+315 -705** 🟩🟥🟥🟥⬛ --- As outlined in issue #113015, there are currently 3[^1] large separate routines that “clean” `rustc_middle::ty` data types related to generics & predicates to rustdoc data types. Every single one has their own kinds of bugs. While I've patched a lot of bugs in each of the routines in the past, it's about time to unify them. This PR is only the first in a series. It completely **yanks** the custom “bounds cleaning” of mod `auto_trait` and reuses the routines found in mod `simplify`. As alluded to, `simplify` is also flawed but it's still more complete than `auto_trait`'s routines. [See also my review comment over at `tests/rustdoc/synthetic_auto/bounds.rs`](https://github.com/rust-lang/rust/pull/123340#discussion_r1546900539). This is preparatory work for rewriting “bounds cleaning” from scratch in follow-up PRs in order to finally [fix] #113015. Apart from that, I've eliminated all potential sources of *instability* in the rendered output. See also #119597. I'm pretty sure this fixes #119597. This PR does not attempt to fix [any other issues related to synthetic auto trait impls](https://github.com/rust-lang/rust/issues?q=is%3Aissue+is%3Aopen+label%3AA-synthetic-impls%20label%3AA-auto-traits). However, it's definitely meant to be a *stepping stone* by making `auto_trait` more contributor-friendly. --- * Replace `FxHash{Map,Set}` with `FxIndex{Map,Set}` to guarantee a stable iteration order * Or as a perf opt, `UnordSet` (a thin wrapper around `FxHashSet`) in cases where we never iterate over the set. * Yes, we do make use of `swap_remove` but that shouldn't matter since all the callers are deterministic. It does make the output less “predictable” but it's still better than before. Ofc, I rely on `rustc_infer` being deterministic. I hope that holds. * Utilizing `clean::simplify` over the custom “bounds cleaning” routines wipes out the last reference to `collect_referenced_late_bound_regions` in rustdoc (`simplify` uses `bound_vars`) which was a source of instability / unpredictability (cc #116388) * Remove the types `RegionTarget` and `RegionDeps` from `librustdoc`. They were duplicates of the identical types found in `rustc`. Just import them from `rustc`. For some reason, they were duplicated when splitting `auto_trait` in two in #49711. * Get rid of the useless “type namespace” `AutoTraitFinder` in `librustdoc` * The struct only held a `DocContext`, it was over-engineered * Turn the associated functions into free ones * Eliminates rightward drift; increases legibility * `rustc` also contains a useless `AutoTraitFinder` struct but I plan on removing that in a follow-up PR * Rename a bunch of methods to be way more descriptive * Eliminate `use super::*;` * Lead to `clean/mod.rs` accumulating a lot of unnecessary imports * Made `auto_traits` less modular * Eliminate a custom `TypeFolder`: We can just use the rustc helper `fold_regions` which does that for us I plan on adding extensive documentation to `librustdoc`'s `auto_trait` in follow-up PRs. I don't want to do that in this PR because further refactoring & bug fix PRs may alter the overall structure of `librustdoc`'s & `rustc`'s `auto_trait` modules to a great degree. I'm slowly digging into the dark details of `rustc`'s `auto_trait` module again and once I have the full picture I will be able to provide proper docs. --- While this PR does indeed touch `rustc`'s `auto_trait` — mostly tiny refactorings — I argue this PR doesn't need any compiler reviewers next to rustdoc ones since that module falls under the purview of rustdoc — it used to be part of `librustdoc` after all (#49711). Sorry for not having split this into more commits. If you'd like me to I can try to split it into more atomic commits retroactively. However, I don't know if that would actually make reviewing easier. I think the best way to review this might just be to place the master version of `auto_trait` on the left of your screen and the patched one on the right, not joking. r? `@GuillaumeGomez` [^1]: Or even 4 depending on the way you're counting. commit 1012218ba8e641f612e486b925214a4522357bec Author: surechen Date: Fri Mar 22 14:20:31 2024 +0800 t plit astconv's error report code in check functions to mod errors. Move some error report codes to mod `astconv/errors.rs` commit 6c5c48e880ca0668c5c8a8029769636831985137 Author: Oli Scherer Date: Tue Apr 2 12:06:52 2024 +0000 Check that nested statics in thread locals are duplicated per thread. commit 0e682e9875458ebf811206a48b688e07d762d9bb Merge: 2531d08e342 748dba2bafa Author: bors Date: Tue Apr 2 09:41:27 2024 +0000 Auto merge of #123347 - saethlin:only-allow-upstream-llvm-calls, r=Nilstrieb Only allow compiler_builtins to call LLVM intrinsics, not any link_name function This is another case of accidental reliance on `inline(never)` like I rooted out in https://github.com/rust-lang/rust/pull/118770. Without this PR, attempting to build some large programs with `-Zcross-crate-inline-threshold=yes` with a sysroot also compiled with that flag will result in linker errors like this: ``` = note: /usr/bin/ld: /tmp/cargo-installNrfN4T/x86_64-unknown-linux-gnu/release/deps/libcompiler_builtins-d2a9b69f4e45b883.rlib(compiler_builtins-d2a9b69f4e45b883.compiler_builtins.dbbc6c2ca970faa4-cgu.0.rcgu.o): in function `core::panicking::panic_fmt': /home/ben/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/panicking.rs:72:(.text.unlikely._ZN4core9panicking9panic_fmt17ha407cc99e97c942fE+0x31): undefined reference to `rust_begin_unwind' ``` With `-Zcross-crate-inline-threshold=yes` we can inline `panic_fmt` into `compiler_builtins`. Then we end up with a call to an upstream monomorphization, but one that has a `link_name` set. But unlike LLVM's magic intrinsic names, this link name is going to make it to the linker, and then we have a problem. This logic looks scuffed, but also we're doing this in 4 other places. Don't know if that means it's good or bad. https://github.com/rust-lang/rust/blob/1684a753dbca5d23b2e03568e6fbbb48eb72d0e6/compiler/rustc_codegen_cranelift/src/abi/mod.rs#L386 https://github.com/rust-lang/rust/blob/1684a753dbca5d23b2e03568e6fbbb48eb72d0e6/compiler/rustc_ast_passes/src/feature_gate.rs#L306 https://github.com/rust-lang/rust/blob/1684a753dbca5d23b2e03568e6fbbb48eb72d0e6/compiler/rustc_codegen_ssa/src/codegen_attrs.rs#L609 https://github.com/rust-lang/rust/blob/1684a753dbca5d23b2e03568e6fbbb48eb72d0e6/compiler/rustc_codegen_gcc/src/declare.rs#L170 commit 00f7f57159aac2e1df49db2eb87353338bef2bb0 Author: Kai Luo Date: Tue Apr 2 17:25:22 2024 +0800 Fix build on AIX commit 1f2d1420cb1c5c5cb52712955f8ed47dcfda8c67 Author: Kai Luo Date: Tue Apr 2 17:17:13 2024 +0800 Fix linking c++ runtimes on AIX commit 0bb1ec729f5caf04b93a22d6aabda24bf187a12b Author: Guillaume Gomez Date: Tue Apr 2 10:42:32 2024 +0200 Remove redundant code comments commit 2531d08e342f21f83eef90741c871f32ba7aa7d2 Merge: e2cf2cb3038 1dcaf70c0e1 Author: bors Date: Tue Apr 2 07:39:10 2024 +0000 Auto merge of #123336 - workingjubilee:strip-the-trace-off-my-back, r=Nilstrieb Note impact of `-Cstrip` on backtraces It is not always clear to people what the impact of `-Cstrip` options are. They are a common question on sites like StackOverflow, and sometimes people even report bugs with "no backtrace" after deliberately mangling the symbol table. We cannot exhaustively document every permutation, but we should warn people about common effects. commit bb439900ddaeb9ad7119508f9e1b9c7aa07b9f91 Author: Steve Lau Date: Tue Apr 2 14:29:38 2024 +0800 style: fmt commit 6ad96825fcd8002c0e4234b020d2eb3ad79232e0 Author: Steve Lau Date: Tue Apr 2 14:18:31 2024 +0800 fix: build on haiku by adding missing import commit e2cf2cb30388385f0fe6b406a31a3f9841a72a62 Merge: 6bbd8c519af 09ea3f93ee6 Author: bors Date: Tue Apr 2 05:17:28 2024 +0000 Auto merge of #122267 - compiler-errors:closure-like-goals, r=lcnr Eagerly instantiate closure/coroutine-like bounds with placeholders to deal with binders correctly A follow-up to #119849, however it aims to fix a different set of issues. Currently, we have trouble confirming goals where built-in closure/fnptr/coroutine signatures are compared against higher-ranked goals. Currently, we don't support goals like `for<'a> fn(&'a ()): Fn(&'a ())` because we don't expect the self type of goal to reference any bound regions from the goal, because we don't really know how to deal with the double binder of predicate + self type. However, this definitely can be reached (#121653) -- and in fact, it results in post-mono errors in the case of #112347 where the builtin type (e.g. a coroutine) is hidden behind a TAIT. The proper fix here is to instantiate the goal before trying to extract the signature from the self type. See final two commits. r? lcnr commit 6bbd8c519af69ebc30486d6cf02b7f7a52113950 Merge: 31075bb493f 643029693b2 Author: bors Date: Tue Apr 2 03:14:05 2024 +0000 Auto merge of #122945 - andy-k:sorted-vec-example, r=jhpratt improve example on inserting to a sorted vector to avoid shifting equal elements commit 09ea3f93ee6497247172a80ba17493748d08b64c Author: Michael Goulet Date: Sun Mar 10 05:14:11 2024 +0000 Fix obligation param and bless tests commit 5f59b7f7634ac4b05923b47de00f2a13b213af1f Author: Michael Goulet Date: Sat Mar 9 22:12:33 2024 +0000 Instantiate closure-like bounds with placeholders to deal with binders correctly commit f2fd2d8c7080f7a7d770b3e3d27e525250c182dc Author: Michael Goulet Date: Sun Mar 31 17:20:00 2024 -0400 Make sure to insert Sized bound first into clauses list commit 31075bb493f49c2b739ff20b2e96a9045e1eba13 Merge: dd5e502d4b0 347c579648c Author: bors Date: Tue Apr 2 01:08:41 2024 +0000 Auto merge of #123346 - workingjubilee:rollup-ix9qpsi, r=workingjubilee Rollup of 2 pull requests Successful merges: - #123323 (std::thread: set_name change for solaris/illumos.) - #123330 (Pass RUST_BACKTRACE when running docker.) r? `@ghost` `@rustbot` modify labels: rollup commit 748dba2bafab14117f626348b716a824d301cffd Author: Ben Kimock Date: Mon Apr 1 20:17:12 2024 -0400 Only allow upstream calls to LLVM intrinsics, not any link_name function commit 347c579648c76b0d93fdff48a3488f42bcb0942b Merge: 48b2a517fca 652f6f71eb2 Author: Jubilee <46493976+workingjubilee@users.noreply.github.com> Date: Mon Apr 1 17:22:11 2024 -0700 Rollup merge of #123330 - jfgoog:pass-backtrace, r=Kobzol Pass RUST_BACKTRACE when running docker. commit 48b2a517fca479d4b66a97fcfce52738e4e0332d Merge: 1684a753dbc ca36fe310e8 Author: Jubilee <46493976+workingjubilee@users.noreply.github.com> Date: Mon Apr 1 17:22:10 2024 -0700 Rollup merge of #123323 - devnexen:thread_set_name_solaris_fix, r=workingjubilee std::thread: set_name change for solaris/illumos. truncate down to 32 (31 + 1) for solaris/illumos. commit bc8c3eff6b821298d96055f27a55985a573af44f Author: Ramon de C Valle Date: Fri Mar 29 17:13:19 2024 -0700 CFI: Change type transformation to use TypeFolder Change type transformation to use TypeFolder. commit 069e7f2a768874536e8688e3df120cd74cd03b0b Author: León Orell Valerian Liehr Date: Sun Mar 31 20:37:35 2024 +0200 rustdoc: heavily simplify synthesis of auto trait impls commit dd5e502d4b095be205c07da1481846f0a07c43b4 Merge: 1684a753dbc 03dd3293550 Author: bors Date: Mon Apr 1 23:02:17 2024 +0000 Auto merge of #123327 - BoxyUwU:param_env_docs_rewrite, r=compiler-errors Update `ParamEnv` docs There is now a wealth of information in the dev guide about `ParamEnv` so we should explicitly link to it from the doc comments. I also added a caution against using `ParamEnv` and removed the comment about it being "suitable for type checking" as you should practically never use `ParamEnv::empty` for type checking r? `@compiler-errors` commit 5a887942a5e50f20569be6569ba62d48a0cc6b53 Author: Ralf Jung Date: Tue Apr 2 00:06:04 2024 +0200 x.py test: remove no-op --skip flag commit 3b1b654f5650a91b5a3ccd7b6c367e43b2196edb Merge: 95c45be1ed6 989f04d0669 Author: bors Date: Mon Apr 1 21:57:50 2024 +0000 Auto merge of #12613 - y21:vacation, r=dswij Pause review rotation for y21 Exams are coming up for me and I want to focus on that, so I'd like to pause PR assignments to me for a bit until that's over (will still continue to look at PRs that are assigned to me and might steal some PRs to review if I find the time). :) -------------- changelog: none commit b390f2f458da288b1be2955bd310a0d45f3bf3ec Author: Ralf Jung Date: Mon Apr 1 23:55:50 2024 +0200 set miri sysroots inside Cargo::new commit ca36fe310e8970a66461ff7fc8496a76c75b70ed Author: David Carlier Date: Mon Apr 1 14:28:37 2024 +0100 std::thread: set_name change for solaris/illumos. truncate down to 32 (31 + 1) for solaris/illumos. commit 95c45be1ed6f1f2025a3c7c806ffca9f9ad18ed2 Merge: 2b30a5924e1 eee4db928fa Author: bors Date: Mon Apr 1 21:14:32 2024 +0000 Auto merge of #12603 - m-rph:12594, r=Manishearth Elide unit variables linted by `let_unit` and use `()` directly instead Situation: `let_unit` lints when an expression binds a unit (`()`) to a variable. In some cases this binding may be passed down to another function. Currently, the lint removes the binding without considering usage. fixes: #12594 changelog: Suggestion Fix [`let_unit`]. Clippy will remove unit bindings and replace all their instances in the body with `()`. commit 1dcaf70c0e114aabb116114dac521f1014204527 Author: Jubilee Young Date: Mon Apr 1 14:13:56 2024 -0700 Note -Cstrip is not a security measure commit 4994f73a27260e837af98ef906d30d6e512bc52f Author: Jubilee Young Date: Mon Apr 1 14:07:59 2024 -0700 Revise strip-symbols paragraph commit 1684a753dbca5d23b2e03568e6fbbb48eb72d0e6 Merge: b38b6caa3e4 71077482d58 Author: bors Date: Mon Apr 1 21:02:53 2024 +0000 Auto merge of #123320 - WaffleLapkin:fixup-never-type-options, r=compiler-errors Fixup parsing of `rustc_never_type_options` attribute #122843 had a copy paste error, which I did not caught when testing. r? `@compiler-errors` commit 973663db9dbff6c9fda1ad79884cff5a1675cb06 Author: Jubilee Young Date: Mon Apr 1 13:50:29 2024 -0700 Discourage stripping symbols in devtools commit 989f04d0669ea04510d60f1727415c0dbed6d6b6 Author: y21 <30553356+y21@users.noreply.github.com> Date: Mon Apr 1 22:35:54 2024 +0200 pause review rotation for y21 commit 59120d0ef54fdb860cb88a9169eb3ff71d2799b3 Author: Guillaume Gomez Date: Mon Apr 1 21:11:22 2024 +0200 Update to new browser-ui-test version commit 652f6f71eb2f0e09849cf416b4d33cb1078846eb Author: James Farrell Date: Mon Apr 1 16:56:43 2024 +0000 Pass RUST_BACKTRACE when running docker. commit cbd593ed18fa455093cd198893a118c1262ff1ca Author: León Orell Valerian Liehr Date: Mon Apr 1 07:12:22 2024 +0200 rustdoc: synthetic impls: auto traits: Fx{Hash↦Index}{Map,Set} commit 3896f0762715092a4f6dfc81b38ba69080192bcd Author: Jubilee Young Date: Mon Apr 1 12:44:49 2024 -0700 Note impact of `-Cstrip` on backtraces It is not always clear to people what the impact of `-Cstrip` options are. They are a common question on sites like StackOverflow, and sometimes people even report bugs with "no backtrace" after deliberately mangling the symbol table. We cannot exhaustively document every permutation, but we should warn people about common effects. commit 03dd3293550c68fcdd00b011fcaa4132084f8a03 Author: Boxy Date: Mon Apr 1 19:59:05 2024 +0100 maybe commit b38b6caa3e42c76d73dc9a60e6dc34af1f20ed15 Merge: a7e3b1c8c59 edb7aeafba0 Author: bors Date: Mon Apr 1 18:42:58 2024 +0000 Auto merge of #123318 - TomAFrench:patch-1, r=GuillaumeGomez chore: fix footnotes/links in `platform-support.md` A missing newline between two references is resulting in some links/footnotes not rendering properly. This PR fixes this. commit 2b30a5924e1fcc3fcba940e55ece26d73fe16d3c Merge: 41e814a554e b456ed31e47 Author: bors Date: Mon Apr 1 17:42:08 2024 +0000 Auto merge of #11996 - J-ZhengLi:issue11992, r=xFrednet,ARandomDev99 fix suggestion for [`len_zero`] with macros fixes: #11992 changelog: fix suggestion for [`len_zero`] with macros commit b456ed31e474c65bfc8f80c71dd0762b6a434857 Author: J-ZhengLi Date: Tue Apr 2 01:27:17 2024 +0800 fix suggestion for [`len_zero`] with macros commit a7e3b1c8c5964113d0866b11114cbdaffd779a2e Merge: c518e5aeecd 747d19326b3 Author: bors Date: Mon Apr 1 16:38:55 2024 +0000 Auto merge of #123315 - devnexen:thread_get_name_solaris, r=ChrisDenton std::thread: adding get_name implementation for solaris/illumos. THREAD_NAME_MAX is 32 (31 max + 1 for the null terminator). commit a91f6221b95dd21394fae4fd1ca45e56475b355f Author: Boxy Date: Mon Apr 1 17:29:34 2024 +0100 Update `ParamEnv` docs commit 41e814a554e4eb2dcd65024bf7e43ffdbe504087 Merge: c2681f2338c 24d20b4eae2 Author: bors Date: Mon Apr 1 16:16:16 2024 +0000 Auto merge of #12605 - xFrednet:00000-ignore-ice, r=y21 Ignore `rustc-ice-` files When ui-test detects a crash, it writes it into a `rustc-ice-*` file. I find it a bit annoying that git always want's to add them. So this just adds these files to our `.gitignore` :D --- changelog: none commit fad8213150b2973f9cccb7b1e09cdf5ff7cdded8 Author: 许杰友 Jieyou Xu (Joe) Date: Thu Mar 28 16:48:35 2024 +0000 Rebless allow list commit fd7bc593634a3402a38ebe8c8d35287139e19a86 Author: 许杰友 Jieyou Xu (Joe) Date: Wed Mar 27 23:34:16 2024 +0000 Port argument-non-c-like-enum to Rust commit 142d02d472246c25ecfa9d7d767c3a97785f010f Author: 许杰友 Jieyou Xu (Joe) Date: Wed Mar 27 23:33:48 2024 +0000 Modify compiletest and run-make-support to support CC invocations in rmake.rs commit 24d20b4eae2646e79a7aba6a691ef39127431882 Author: xFrednet Date: Mon Apr 1 17:05:55 2024 +0200 Set `RUSTC_ICE=0` in uitests and `cargo dev lint` commit c518e5aeecd3421a8ed5acb96946badb217dc64f Merge: 6bb6b816bfc d7b55e4c90b Author: bors Date: Mon Apr 1 14:35:38 2024 +0000 Auto merge of #123265 - joboet:guardians_of_the_unix, r=ChrisDenton Refactor stack overflow handling Currently, every platform must implement a `Guard` that protects a thread from stack overflow. However, UNIX is the only platform that actually does so. Windows has a different mechanism for detecting stack overflow, while the other platforms don't detect it at all. Also, the UNIX stack overflow handling is split between `sys::pal::unix::stack_overflow`, which implements the signal handler, and `sys::pal::unix::thread`, which detects/installs guard pages. This PR cleans this by getting rid of `Guard` and unifying UNIX stack overflow handling inside `stack_overflow` (commit 1). Therefore we can get rid of `sys_common::thread_info`, which stores `Guard` and the current `Thread` handle and move the `thread::current` TLS variable into `thread` (commit 2). The second commit is not strictly speaking necessary. To keep the implementation clean, I've included it here, but if it causes too much noise, I can split it out without any trouble. commit c2681f2338c2fbeaeb87cda1c19bc7e9cd95f2f4 Merge: cb87a574fd5 91f514cc836 Author: bors Date: Mon Apr 1 14:10:40 2024 +0000 Auto merge of #12453 - y21:span_lint_into_diag, r=blyxyas accept `String` in `span_lint*` functions directly to avoid unnecessary clones context: https://rust-lang.zulipchat.com/#narrow/stream/257328-clippy/topic/Accepting.20.60Into.3C.7BSub.7DdiagMessage.3E.60.20in.20.60span_lint*.60.20functions/near/425703273 tldr: the `span_lint*` functions now accept both `String`s, which are then reused and not recloned like before, and also `&'static str`, in which case it [doesn't need to allocate](https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_error_messages/lib.rs.html#359). Previously, it accepted `&str` and would always call `.to_string()`, which is worse in any case: it allocates for `&'static str` and forces a clone even if the caller already has a `String`. --------- This PR has a massive diff, but the only interesting change is in the first commit, which changes the message/help/note parameter in the `span_lint*` functions to not take a `&str`, but an `impl Into`. The second commit changes all of the errors that now occur: - `&format!(...)` cannot be passed to `span_lint` anymore. Instead, we now simply pass `format!()` directly. - `Into` can be `&'static str`, but not any `&str`. So this requires changing a bunch of other `&str` to `&'static str` at call sites as well. - Added [`Sugg::into_string`](https://github.com/y21/rust-clippy/blob/9fc88bc2851fbb287d89f65b78fb67af504f8362/clippy_utils/src/sugg.rs#L362), which, as opposed to `Sugg::to_string`, can take advantage of the fact that it takes ownership and is able to reuse the `String` changelog: none commit d7b55e4c90b6723d95b7cb8c152660b2ef7afcba Author: joboet Date: Mon Apr 1 15:28:27 2024 +0200 update comment commit 91f514cc8360b369a7fe5f6108aae025b22a38db Author: y21 <30553356+y21@users.noreply.github.com> Date: Sat Mar 23 06:52:11 2024 +0100 fix fallout from previous commit commit bd4d4561386c5b1219d8e8830869d39dea8149ed Author: y21 <30553356+y21@users.noreply.github.com> Date: Sat Mar 23 06:51:42 2024 +0100 accept Into<{Sub}DiagMessage> in span_lint functions commit 6bb6b816bfcf9e26fac5175e3e907dcefe5ecdbc Merge: 3d5528c2878 7410f78e9a3 Author: bors Date: Mon Apr 1 12:31:27 2024 +0000 Auto merge of #122046 - Nadrieril:integrate-or-pats2, r=matthewjasper match lowering: handle or-patterns one layer at a time `create_or_subcandidates` and `merge_trivial_subcandidates` both call themselves recursively to handle nested or-patterns, which is hard to follow. In this PR I avoid the need for that; we now process a single "layer" of or-patterns at a time. By calling back into `match_candidates`, we only need to expand one layer at a time. Conversely, since we always try to simplify a layer that we just expanded (thanks to https://github.com/rust-lang/rust/pull/123067), we only have to merge one layer at a time. r? `@matthewjasper` commit eee4db928fa745fba934e008be01765c22abb4de Author: Quinn Sinclair Date: Sun Mar 31 17:53:13 2024 +0200 Replace elided variable in `let_unit` with `()` when used Situation: `let_unit` lints when an expression binds a unit (`()`) to a variable. In some cases this binding may be passed down to another function. Currently, the lint removes the binding without considering usage. Change: All usages of the elided variable are now replaced with `()`. fixes: #12594 commit 71077482d58e807b9d09637237015df3f3d7d690 Author: Maybe Waffle Date: Mon Apr 1 10:56:33 2024 +0000 Fixup parsing of `rustc_never_type_options` attribute Copy paste error strike again.. commit edb7aeafba0a7a93408e81e8ffeb99317e97cbba Author: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon Apr 1 11:43:52 2024 +0100 chore: fix footnotes/links in `platform-support.md` commit 17a61b261061365e9708478ccf95a8103dbc1944 Author: xFrednet Date: Mon Apr 1 12:03:16 2024 +0200 Ignore `rustc-ice-` files commit 3d5528c287860b918e178a34f04ff903325571b3 Merge: 871df0d13ae 4ff8a9bd6b6 Author: bors Date: Mon Apr 1 09:22:01 2024 +0000 Auto merge of #123310 - compiler-errors:nested-static-codegen-attrs, r=oli-obk Don't inherit codegen attrs from parent static Putting this up partly for discussion and partly for review. Specifically, in #121644, `@oli-obk` designed a system that creates new static items for representing nested allocations in statics. However, in that PR, oli made it so that these statics inherited the codegen attrs from the parent. This causes problems such as colliding symbols with `#[export_name]` and ICEs with `#[no_mangle]` since these synthetic statics have no `tcx.item_name(..)`. So the question is, is there any case where we *do* want to inherit codegen attrs from the parent? The only one that seems a bit suspicious is the thread-local attribute. And there may be some interesting interactions with the coverage attributes as well... Fixes (after backport) #123274. Fixes #123243. cc #121644. r? `@oli-obk` cc `@nnethercote` `@RalfJung` (reviewers on that pr) commit 747d19326b3e9a87eca74947afb97cca74b843fa Author: David Carlier Date: Mon Apr 1 10:01:21 2024 +0100 std::thread: adding get_name implementation for solaris/illumos. THREAD_NAME_MAX is 32 (31 max + 1 for the null terminator). commit cb87a574fd5d70293531856619a05403c13ca74f Merge: 797d50dfe6f 0f63fa8c333 Author: bors Date: Mon Apr 1 07:57:26 2024 +0000 Auto merge of #12601 - Alexendoo:box-default-style, r=llogiq Move `box_default` to style, do not suggest turbofishes `Box::default()` [had its `#[rustc_box]` attribute removed](https://github.com/rust-lang/rust/pull/108476/files#r1120930164) in 1.69 so is no longer a perf related lint The lint is moved to style but no longer produces suggestions containing turbofishes, as they're often longer/more annoying to type changelog: [`box_default`]: Move to style r? `@llogiq` commit 871df0d13ae55fcfd8c880e99fedbaba35952e1c Merge: 7f84ede33d6 85d460e829c Author: bors Date: Mon Apr 1 07:19:57 2024 +0000 Auto merge of #123192 - RalfJung:bootstrap-test-miri, r=onur-ozkan Refactor the way bootstrap invokes `cargo miri` Instead of basically doing `cargo run --manifest-path= -- miri`, let's invoke the `cargo-miri` binary directly. That means less indirections, and also makes it easier to e.g. run the libcore test suite in Miri. (But there are still other issues with that.) Also also adjusted Miri's stage numbering so that it is consistent with rustc/rustdoc. This also makes `./x.py test miri` honor `--no-doc`. And this fixes https://github.com/rust-lang/rust/issues/123177 by moving where we handle parallel_compiler. commit 7f84ede33d60599a4a97697cdd59e8bf96c85677 Merge: defef8658e8 0bbaa2505be Author: bors Date: Mon Apr 1 05:18:51 2024 +0000 Auto merge of #122663 - beetrees:non-unicode-env-error, r=TaKO8Ki Fix error message for `env!` when env var is not valid Unicode Currently (without this PR) the `env!` macro emits an ```environment variable `name` not defined at compile time``` error when the environment variable is defined, but not a valid Unicode string. This PR introduces a separate more accurate error message, and a test to verify this behaviour. For reference, before this PR, the new test would have outputted: ``` error: environment variable `NON_UNICODE_VAR` not defined at compile time --> non_unicode_env.rs:2:13 | 2 | let _ = env!("NON_UNICODE_VAR"); | ^^^^^^^^^^^^^^^^^^^^^^^ | = help: use `std::env::var("NON_UNICODE_VAR")` to read the variable at run time = note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error ``` whereas with this PR, the test ouputs: ``` error: environment variable `NON_UNICODE_VAR` is not a valid Unicode string --> non_unicode_env.rs:2:13 | 2 | let _ = env!("NON_UNICODE_VAR"); | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error ``` commit 0bbaa2505be6d7e6483a0dcbe585c96684e73da5 Author: beetrees Date: Sun Mar 17 21:12:17 2024 +0000 Fix error message for `env!` when env var is not valid Unicode commit defef8658e8f740cc8d2818b5b96441071e9a7a6 Merge: 66de6111960 6e5f1dacf3a Author: bors Date: Mon Apr 1 03:16:45 2024 +0000 Auto merge of #122972 - beetrees:use-align-type, r=fee1-dead Use the `Align` type when parsing alignment attributes Use the `Align` type in `rustc_attr::parse_alignment`, removing the need to call `Align::from_bytes(...).unwrap()` later in the compilation process. commit 4ff8a9bd6b64e32703603cf8bc8cb5cb221d4889 Author: Michael Goulet Date: Sun Mar 31 22:33:36 2024 -0400 Don't inherit codegen attrs from parent static commit 6e5f1dacf3a670cf03111039a032c765355839aa Author: beetrees Date: Sun Mar 24 01:03:39 2024 +0000 Use the `Align` type when parsing alignment attributes commit 56dbeeb5ac05d8e34983db20453d72bdeb222cbe Author: Michael Goulet Date: Sun Mar 31 21:03:59 2024 -0400 Add regression tests for 123303 commit 88296bddf87d927228c5c8ff4d3ad6fa59ff9362 Author: Michael Goulet Date: Wed Mar 20 15:47:00 2024 -0400 Remove EvaluatedToErrStackDependent commit b8396d10c42cf758bcf901019181a0d47e56ed19 Author: Michael Goulet Date: Fri Oct 6 20:27:30 2023 +0000 Always make inductive cycles as ambig during typeck commit 66de6111960571ab7b030c205b6176801c7ecbfc Merge: 80581365024 418535b7989 Author: bors Date: Sun Mar 31 22:39:04 2024 +0000 Auto merge of #123270 - JaniM:janim/string-alloc-doc, r=workingjubilee doc: mention heap allocation earlier in String docs Just a tiny addition. Helps with #123263. commit 27704c7f9eea6e8ffaedac04ae1c932da7422fb7 Author: Nadrieril Date: Sun Mar 31 23:57:53 2024 +0200 Fix union handling in exhaustiveness commit db9b4eac480525d7af188a8360371d3cef84de98 Author: Nadrieril Date: Sun Mar 31 23:57:47 2024 +0200 Add tests commit 85d460e829c5ca2600fed6b432a329dbf6d18689 Author: Ralf Jung Date: Sun Mar 31 23:10:47 2024 +0200 checktools: make it easier to trace what is happening commit 8cf2c0dc67d880e54be03ebb08f7f960c21bb794 Author: Nadrieril Date: Fri Mar 29 19:40:54 2024 +0100 Improve debugging experience commit b08b06e3a861e237a5bd961a568291eb0cd4e833 Author: Ralf Jung Date: Sun Mar 31 23:09:33 2024 +0200 fix not finding the right libraries on Windows commit 418535b798946b29c70a3ac4e33bbc6fbb4fb132 Author: Jani Mustonen Date: Mon Apr 1 00:04:57 2024 +0300 doc: mention heap allocation earlier in String docs Just a tiny addition. Helps with #123263. commit 805813650248c1a2f6f271460d728d1bb852d2a7 Merge: 204805a0923 f7d0a59a7a7 Author: bors Date: Sun Mar 31 20:35:15 2024 +0000 Auto merge of #123299 - workingjubilee:rollup-2z8amaj, r=workingjubilee Rollup of 5 pull requests Successful merges: - #123180 (Rewrite `core-no-fp-fmt-parse` test in Rust) - #123267 (std::thread: adding get_name haiku implementation.) - #123268 (warn against implementing Freeze) - #123271 (doc: describe panic conditions for SliceIndex implementations) - #123295 (add myself to compiler review rotation) r? `@ghost` `@rustbot` modify labels: rollup commit f7d0a59a7a731964de9ac306e372a4e20188f76b Merge: 42ca32673a4 8d67fb99fc1 Author: Jubilee <46493976+workingjubilee@users.noreply.github.com> Date: Sun Mar 31 13:18:18 2024 -0700 Rollup merge of #123295 - BoxyUwU:boxy_compiler_reviews, r=Mark-Simulacrum add myself to compiler review rotation title commit 42ca32673a44361b4fb30b53f780f9c7ef1fba2e Merge: 9ff4c704763 4ca31515680 Author: Jubilee <46493976+workingjubilee@users.noreply.github.com> Date: Sun Mar 31 13:18:17 2024 -0700 Rollup merge of #123271 - JaniM:janim/sliceindex-doc, r=Nilstrieb doc: describe panic conditions for SliceIndex implementations Implementation note: The most probable place for users to find the documentation is at https://doc.rust-lang.org/std/slice/trait.SliceIndex.html On that page, documentation added to specific methods will not be visible. As such, I opted to add the comments to the impl blocks directly. Helps with #121568. commit 9ff4c704763733e5711a0bdf8bb509b0bccf5fee Merge: 2a08f020489 602401c4d49 Author: Jubilee <46493976+workingjubilee@users.noreply.github.com> Date: Sun Mar 31 13:18:17 2024 -0700 Rollup merge of #123268 - RalfJung:dont-freeze, r=Nilstrieb warn against implementing Freeze As [requested](https://github.com/rust-lang/rust/pull/123184#issuecomment-2028531388) by `@workingjubilee` commit 2a08f020489416086d39f03e91236aaa66e94507 Merge: 17737bfece6 e5c5ed00a5e Author: Jubilee <46493976+workingjubilee@users.noreply.github.com> Date: Sun Mar 31 13:18:17 2024 -0700 Rollup merge of #123267 - devnexen:thread_get_name_haiku, r=joboet std::thread: adding get_name haiku implementation. follow-up #123233 commit 17737bfece68f77749961275b0278500148c7574 Merge: bf71daedc29 e477488267e Author: Jubilee <46493976+workingjubilee@users.noreply.github.com> Date: Sun Mar 31 13:18:16 2024 -0700 Rollup merge of #123180 - Oneirical:master, r=Mark-Simulacrum Rewrite `core-no-fp-fmt-parse` test in Rust Claiming the simple "core-no-fp-fmt-parse" test from #121876. `run_make_support` was altered with `arg_path` written in #121918 by `@abhay-51,` with additional doc comment. Preliminary GSoC contribution for the project proposal mentored by `@jieyouxu.` commit 602401c4d49753dfd9d351ffcf0c72c00ff4c62f Author: Ralf Jung Date: Sun Mar 31 12:46:37 2024 +0200 warn against implementing Freeze commit 8d67fb99fc1d4e93c441ac220fc75e19140615a0 Author: Boxy Date: Sun Mar 31 20:18:37 2024 +0100 beep boop commit 204805a0923d765494edf643acc16c531788b0f0 Merge: bf71daedc29 42972f52de2 Author: bors Date: Sun Mar 31 18:34:26 2024 +0000 Auto merge of #123266 - RalfJung:catch-panic, r=workingjubilee catch_panic: warn about panicking payload drop Warns about the footgun in https://github.com/rust-lang/rust/issues/86027. Will be unnecessary if panics escaping from drop leads to abort (https://github.com/rust-lang/rfcs/pull/3288). But until that is enforced everywhere, let's warn users about this. commit 4c0aea0d47d9e01642ee59a8620795558f098408 Author: Caio Date: Sun Mar 31 14:58:17 2024 -0300 Move some tests commit 4797fba3b71fc44b0904ebd52603a953609a9eb6 Author: Ralf Jung Date: Sat Mar 30 15:09:38 2024 +0100 add FIXME for making the cargo cmd properly typed commit 288daeb14f70192d53d2331b4c43a8c6d90d9d7e Author: Ralf Jung Date: Sat Mar 30 13:52:32 2024 +0100 move parallel_compiler handling into prepare_tool_cargo so that it is done everywhere commit 7ac5f604c1601c0c43cd305ae43c78795c1eac2b Author: Ralf Jung Date: Sat Mar 30 13:37:32 2024 +0100 remove a pointless env var CARGO_EXTRA_FLAGS is respected by the ./miri script which we are not invoking here commit fb8abe5fcfca0ad43279e7f62ad14b8604d62efc Author: Ralf Jung Date: Sat Mar 30 13:27:17 2024 +0100 shift Miri's stage so that it matches other rustc-based tools commit b5fe655ae855c72d49f26103ec4717995c91ff02 Author: Ralf Jung Date: Fri Mar 29 12:27:20 2024 +0100 bootstrap/rustc: remove a miri hack commit 4056df5cf74e83286417f0df83cba510c32488d3 Author: Ralf Jung Date: Fri Mar 29 12:27:07 2024 +0100 cargo-miri: better debug output; reorder a comment to make it less confusing commit e5c5ed00a5e8ecf453dca2072d54e51316594a5e Author: David Carlier Date: Sun Mar 31 11:39:06 2024 +0100 std::thread: adding get_name haiku implementation. follow-up #123233 commit 0f63fa8c3332d4c678396ab019a1b24bd6373a5f Author: Alex Macleod Date: Sun Mar 31 13:22:04 2024 +0000 Move box_default to style, do not suggest turbofishes `Box::default()` had its `#[rustc_box]` attribute removed in 1.69 so is no longer a perf related lint The lint is moved to style but no longer produces suggestions containing turbofishes, as they're often longer/more annoying to type commit bf71daedc29e7a240261acd1516378047e311a6f Merge: a8cfc838013 7e4bc4a3735 Author: bors Date: Sun Mar 31 16:22:38 2024 +0000 Auto merge of #121851 - michaelwoerister:mcp-533-effective-vis, r=cjgillot Use FxIndexMap instead FxHashMap to stabilize iteration order in EffectiveVisibilities Part of [MCP 533](https://github.com/rust-lang/compiler-team/issues/533). commit 41434ff4a3592f5ae9edcb057dca4ae09824eac7 Author: joboet Date: Sun Mar 31 15:38:22 2024 +0200 refer to a different module in UI test commit 4ca315156805d951663e66c13f395d7f405c9ed2 Author: Jani Mustonen Date: Sun Mar 31 16:05:34 2024 +0300 doc: describe panic conditions for SliceIndex implementations Implementation note: The most probable place for users to find the documentation is at https://doc.rust-lang.org/std/slice/trait.SliceIndex.html On that page, documentation added to specific methods will not be visible. As such, I opted to add the comments to the impl blocks directly. Helps with #121568. commit a8cfc83801301c2b4f0fd030192e268eeb15d473 Merge: 395f780cd8d 877e8d456db Author: bors Date: Sun Mar 31 12:36:23 2024 +0000 Auto merge of #123246 - Kobzol:tarball-reproducible, r=Mark-Simulacrum Make source tarball generation more reproducible This PR performs several changes to source tarball generation (`x dist rustc-src`) in order to make it more reproducible (in light of the recent "xz backdoor"...). I want to follow up on it with making a separate CI workflow for generating the tarball. After this PR, running this locally produces identical checksums: ```bash $ ./x dist rustc-src $ sha256sum build/dist/rustc-1.79.0-src.tar.gz $ ./x dist rustc-src $ sha256sum build/dist/rustc-1.79.0-src.tar.gz ``` r? `@Mark-Simulacrum` commit 797d50dfe6f4bbfdd30510e9d8b326da17f1213c Merge: 3787a0ccb8e 60937bf2dbf Author: bors Date: Sun Mar 31 12:16:01 2024 +0000 Auto merge of #12562 - m-rph:12501, r=y21 Allow `filter_map_identity` when the closure is typed This extends the `filter_map_identity` lint to support typed closures. For untyped closures, we know that the program compiles, and therefore we can safely suggest using flatten. For typed closures, they may participate in type resolution. In this case we use `Applicability::MaybeIncorrect`. Details: https://rust-lang.zulipchat.com/#narrow/stream/257328-clippy/topic/Should.20.60filter_map_identity.60.20lint.20when.20closures.20are.20typed.3F changelog: `filter_map_identity` will now suggest using flatten for typed closures. r? `@y21` && `@Centri3` commit 877e8d456db497c2b7b895cb88243fb894584701 Author: Jakub Beránek Date: Sat Mar 30 22:03:11 2024 +0100 Sort directories when generating tarballs commit 18d9d44bd6f431db4e50f5636b08458a8a1a63c4 Author: Jakub Beránek Date: Sat Mar 30 21:27:58 2024 +0100 Make tarball generation more deterministic commit 8caef4e6c30cd89496ab077e271cd0e9c03fe970 Author: Jakub Beránek Date: Sat Mar 30 21:21:58 2024 +0100 Remove potential `__pycache__` directories from src tarballs commit 63d6ce03b341ba2c42782099dcfae90e3daa5021 Author: Jakub Beránek Date: Sat Mar 30 19:58:30 2024 +0100 Checkout all submodules when building source tarballs commit 6f4f39a8d56968a1ea120e6903c0640eb2a13ee9 Author: Jakub Beránek Date: Sat Mar 30 19:56:08 2024 +0100 Move submodule lookup to `Builder` commit 395f780cd8da001b35d7e588177bbad82d5cf8fa Merge: 688c30dc9f8 9abf4bcadb3 Author: bors Date: Sun Mar 31 10:26:55 2024 +0000 Auto merge of #123264 - matthiaskrgr:rollup-smyy7j9, r=matthiaskrgr Rollup of 4 pull requests Successful merges: - #123189 (Log BOLT args in bootstrap `rustc` shim) - #123211 (Stop calling visitors `V`) - #123242 (pattern analysis: Require enum indices to be contiguous) - #123260 (Miri subtree update) r? `@ghost` `@rustbot` modify labels: rollup commit 42972f52de2aedbc8cc96a3f4b28c4330822aecd Author: Ralf Jung Date: Sun Mar 31 11:54:16 2024 +0200 catch_panic: warn about panicking payload drop commit 9abf4bcadb350e1688d0dc7ee1eef517702fcf1a Merge: f04d068adc3 d5de305fef8 Author: Matthias Krüger Date: Sun Mar 31 11:50:41 2024 +0200 Rollup merge of #123260 - RalfJung:miri, r=RalfJung Miri subtree update r? `@ghost` commit f04d068adc33c17249baf5ddef98548b514bf1ee Merge: d77608b4b9d e1b8441899b Author: Matthias Krüger Date: Sun Mar 31 11:50:41 2024 +0200 Rollup merge of #123242 - Nadrieril:require-contiguous-enum-indices, r=compiler-errors pattern analysis: Require enum indices to be contiguous We had a cfg-hack to allow rust-analyzer to use non-contiguous indices for its enum variants. Unfortunately this no longer works if r-a uses the in-tree version of the crate. This PR removes the hack, and on the r-a side we'll have to use contiguous indices but that's not too hard. r? `@compiler-errors` commit d77608b4b9deeca2cf2041586322399dcfd78bad Merge: 0928a54a1b0 bda301ead8a Author: Matthias Krüger Date: Sun Mar 31 11:50:40 2024 +0200 Rollup merge of #123211 - compiler-errors:V, r=estebank Stop calling visitors `V` Renames some visitors which currently have the unhelpful name of `V`. It's not self-documenting, and there is no situation where saving a few bytes in source code helps anyone. Stacked on top of #123202 due to conflict. commit 0928a54a1b0f6b1117d40e551f13d6226070dcef Merge: c93b17d6d20 a4087b7915b Author: Matthias Krüger Date: Sun Mar 31 11:50:40 2024 +0200 Rollup merge of #123189 - Kobzol:rustc-shim-log, r=onur-ozkan Log BOLT args in bootstrap `rustc` shim Before, the BOLT argument would not be logged, because it was only added after the logging has happened. Found by `@RalfJung` [here](https://github.com/rust-lang/rust/pull/116352#discussion_r1544235771). commit 76684181018640df1769604cd92ed6beb30a27d6 Author: joboet Date: Sun Mar 31 11:28:24 2024 +0200 std: move `thread::current` TLS variable out of `thread_info` commit 5b9d7ab558fe6ee53bd40e16a20c25716f3c9e56 Author: joboet Date: Sun Mar 31 11:24:33 2024 +0200 std: move UNIX stack overflow guard page handling into `stack_overflow.rs` commit d9f29fa018b7534e14f7452f41667dec3d67e1b7 Author: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun Mar 31 09:09:58 2024 +0000 Rustfmt all scripts commit 7cdec718681936ff4a41ab8de712bfb415636983 Author: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun Mar 31 09:01:37 2024 +0000 Move the rustc testing section out of the readme To make the readme a bit easier to read. commit 688c30dc9f8434d63bddb65bd6a4d2258d19717c Merge: c93b17d6d20 a4e02d70376 Author: bors Date: Sun Mar 31 08:25:24 2024 +0000 Auto merge of #123258 - lnicola:sync-from-ra, r=lnicola Subtree update of `rust-analyzer` commit a4e02d70376d4440dd0a9d78cb558ce328c85868 Merge: 5f358a848cd f5a9250147f Author: Laurențiu Nicola Date: Sun Mar 31 09:57:00 2024 +0300 Merge commit 'f5a9250147f6569d8d89334dc9cca79c0322729f' into sync-from-ra commit c93b17d6d20a234f21e04804adef7b58a08dd9e4 Merge: 5f358a848cd 71ea506d3df Author: bors Date: Sun Mar 31 06:21:53 2024 +0000 Auto merge of #123236 - klensy:tracing-tree-bump, r=Mark-Simulacrum bump tracing-tree to 0.3 Only change in `tracing-tree` is https://github.com/davidbarsky/tracing-tree/pull/76 * dedupes `tracing-log` * dupes `nu-ansi-term` commit d5de305fef8065d3af726537db2513a884ed2bcc Merge: 723acede108 eb8e8c06b6c Author: bors Date: Sun Mar 31 05:44:17 2024 +0000 Auto merge of #3435 - rust-lang:rustup-2024-03-31, r=saethlin Automatic Rustup commit eb8e8c06b6c6ab90b6ecbed1ac3820304c53ef01 Merge: 2dd824789c3 5baf1e13f56 Author: The Miri Cronjob Bot Date: Sun Mar 31 05:40:36 2024 +0000 Merge from rustc commit 2dd824789c34d059ad787fc65ad15ffc668c895e Author: The Miri Cronjob Bot Date: Sun Mar 31 05:33:07 2024 +0000 Preparing for merge from rustc commit 5f358a848cd4a553aae9ea82cf7a9d3660977221 Merge: 5baf1e13f56 c749483e267 Author: bors Date: Sun Mar 31 04:18:47 2024 +0000 Auto merge of #123233 - devnexen:thread_get_name_bsd, r=joboet std::thread: adding freebsd/netbsd to the linux's get_name implementa… …tion. commit 8f5a28e0aa3a84aa105c66acec58684a2550b73a Author: Stepan Koltsov Date: Fri Mar 29 03:24:02 2024 +0000 Require Pointee::Metadata to be Freeze So pointee metadata can be used in anonymous statics. This is prerequisite for implementing ThinBox without allocation for ZST. See https://github.com/rust-lang/rust/pull/123184#discussion_r1544627488 commit 5baf1e13f568b61e121953bf6a3d09faee7dd446 Merge: 1aedc9640cd 65efa5b3b9e Author: bors Date: Sun Mar 31 02:12:50 2024 +0000 Auto merge of #122459 - Nadrieril:sort-eq, r=oli-obk match lowering: sort `Eq` candidates in the failure case too This is a slight tweak to MIR gen of matches. Take a match like: ```rust match (s, flag) { ("a", _) if foo() => 1, ("b", true) => 2, ("a", false) => 3, (_, true) => 4, _ => 5, } ``` If we switch on `s == "a"`, the first candidate matches, and we learn almost nothing about the second candidate. So there's a choice: 1. (what we do today) stop sorting candidates, keep the "b" case grouped with everything below. This could allow us to be clever here and test on `flag == true` next. 2. (what this PR does) sort "b" into the failure case. The "b" will be alone (fewer opportunities for picking a good test), but that means the two "a" cases require a single test. Today, we aren't clever in which tests we pick, so this is an unambiguous win. In a future where we pick tests better, idk. Grouping tests as much as possible feels like a generally good strategy. This was proposed in https://github.com/rust-lang/rust/issues/29623 (9 years ago :D) commit 1aedc9640cd740976f49480c4efefe0c0311fbb9 Merge: 5da1a1b59a5 b110cb3dc6d Author: bors Date: Sun Mar 31 00:09:41 2024 +0000 Auto merge of #123181 - stepancheg:pointee-metadata-debug, r=the8472,Amanieu Require Debug for Pointee::Metadata Useful for debugging. commit 723acede108580cdd6a848342ef3049f687f197e Merge: a8fd61bf13a 10217fdec65 Author: bors Date: Sat Mar 30 22:57:13 2024 +0000 Auto merge of #3434 - RalfJung:stacked-borrows-cache-consistency, r=RalfJung cotrol stacked borrows consistency check with its own feature flag Fixes https://github.com/rust-lang/miri/issues/3431 commit a8fd61bf13aa7ff4a70f884825b6a50035aa1ad2 Merge: f04352a7dd0 7f6d89dae99 Author: bors Date: Sat Mar 30 22:14:41 2024 +0000 Auto merge of #3432 - RalfJung:gc-stress, r=RalfJung run GC stress test only for host tests I suspect these are a significant contributor to our Linux CI job being by far the slowest currently. Let's see. We have Linux, Windows, and macOS hosts so all major OSes are still covered. commit 10217fdec65d0ed1db69708b62da0774edc06087 Author: Ralf Jung Date: Sat Mar 30 23:10:43 2024 +0100 cotrol stacked borrows consistency check with its own feature flag commit 5da1a1b59a52339845804fa39b1a21b7322a513f Merge: 8df7e723ea7 d7d5fc97341 Author: bors Date: Sat Mar 30 21:58:49 2024 +0000 Auto merge of #123085 - tgross35:f16-f128-step4.0-libs-basic-impls, r=Amanieu Add basic trait impls for `f16` and `f128` Split off part of so the compiler doesn't ICE because it expects primitives to have some minimal traits. Fixes commit 3787a0ccb8e69a59ce7474cdff6ee3e39902f568 Merge: cebf879de89 f6c006364bb Author: bors Date: Sat Mar 30 21:26:36 2024 +0000 Auto merge of #11350 - y21:issue11349, r=xFrednet [`type_id_on_box`]: lint on any `Box` Closes #11349. It now not only lints when calling `.type_id()` on the type `Box`, but also on any `Box` where `Trait` is a subtrait of `Any` changelog: FN: [`type_id_on_box`]: lint if `Any` is a sub trait commit 8df7e723ea729a7f917501cc2d91d640b7021373 Merge: 70714e38f22 3855b8bb609 Author: bors Date: Sat Mar 30 19:56:58 2024 +0000 Auto merge of #99322 - GKFX:const-int-parse, r=Mark-Simulacrum Make {integer}::from_str_radix constant This commit makes FromStr on integers constant so that `const x: u32 = "23".parse();` works. More practical use-case is with environment variables at build time as discussed in https://github.com/rust-lang/rfcs/issues/1907. Tracking issue #59133. ACP: https://github.com/rust-lang/libs-team/issues/74 commit f6c006364bbccd6308f6698dae276cd24b842ba9 Author: y21 <30553356+y21@users.noreply.github.com> Date: Sat Mar 30 20:54:16 2024 +0100 split up tests into fixable and unfixable now and add annotations commit 36e4c2083b540132cbcb7761f3133b2ed561ad29 Author: y21 <30553356+y21@users.noreply.github.com> Date: Sat Mar 30 20:52:53 2024 +0100 lint on any `Box`, but provide a suggestion for subtypes of `dyn Any` commit f37a4d55ee6a15a4d240de07a4a33766973866c7 Author: Jules Bertholet Date: Sat Mar 30 12:57:54 2024 -0500 Implement "& everywhere" The original proposal allows reference patterns with "compatible" mutability, however it's not clear what that means so for now we require an exact match. I don't know the type system code well, so if something seems to not make sense it's probably because I made a mistake commit 70714e38f224ef1d50f3f772808fff65d7a29c0b Merge: ef493651025 8cc9a912d7d Author: bors Date: Sat Mar 30 17:56:26 2024 +0000 Auto merge of #123106 - maurer:cfi-closures, r=compiler-errors CFI: Abstract Closures and Coroutines This will abstract coroutines in a moment, it's just abstracting closures for now to show `@rcvalle` This uses the same principal as the methods on traits - figure out the `dyn` type representing the fn trait, instantiate it, and attach that alias set. We're essentially just computing how we would be called in a dynamic context, and attaching that. commit cebf879de895deae27be8593692ce54023d7f48e Merge: e0e7ee183f6 f2e91ab1b9a Author: bors Date: Sat Mar 30 17:50:36 2024 +0000 Auto merge of #12312 - pitaj:legacy_numeric_constants, r=xFrednet new lint `legacy_numeric_constants` Rework of #10997 - uses diagnostic items - does not lint imports of the float modules (`use std::f32`) - does not lint usage of float constants that look like `f32::MIN` I chose to make the float changes because the following pattern is actually pretty useful ```rust use std::f32; let omega = freq * 2 * f32::consts::PI; ``` and the float modules are not TBD-deprecated like the integer modules. Closes #10995 --- changelog: New lint [`legacy_numeric_constants`] [#12312](https://github.com/rust-lang/rust-clippy/pull/12312) commit 7f6d89dae99310787def4dae3e3e871b0dbe5990 Author: Ralf Jung Date: Sat Mar 30 18:45:38 2024 +0100 move tests away from the slow Windows builder commit 65efa5b3b9ec77be6a009a08ea07971d6438ec9b Author: Nadrieril Date: Wed Mar 27 17:50:58 2024 +0100 Add FileCheck directives to the new tests. commit 9f6c675238b97c2db41b734dc5e420248ca8055a Author: Ralf Jung Date: Sat Mar 30 17:58:34 2024 +0100 run GC stress test only for host tests commit 8cc9a912d7daf092adc53f0b6b7fac5e86fa7e0a Author: Matthew Maurer Date: Tue Mar 26 17:00:57 2024 +0000 CFI: Rewrite closure and coroutine instances to their trait method Similar to methods on a trait object, the most common way to indirectly call a closure or coroutine is through the vtable on the appropriate trait. This uses the same approach as we use for trait methods, after backing out the trait arguments from the type. commit e974570c42f4460b30fce11003b7891435931cbd Author: Matthew Maurer Date: Wed Mar 27 15:50:53 2024 +0000 CFI: Only encode Coroutine Parent Args Fixes #122705 commit 75d2e67ed240731c15b697424bbd671464c4bde6 Author: Nadrieril Date: Wed Mar 13 22:23:45 2024 +0100 Sort `Eq` candidates in the failure case too commit 5ef9ad37abf51d7346f7ac68b910f1a18d86f65b Author: Nadrieril Date: Wed Mar 13 22:20:40 2024 +0100 Add test commit e67f5294df55176213d0f1e8db0e8a27fd0155dc Author: Nadrieril Date: Wed Mar 13 15:04:07 2024 +0100 Regroup mir-opt tests of match building commit 2a939422ca7471317b7b0d617272e6c18ca5a291 Author: Ralf Jung Date: Thu Mar 28 23:03:31 2024 +0100 prepare_tool_cargo: add support for a miri-test mode, and use it in the cargo-miri smoke test and Miri sysroot build commit c749483e2670162a0d185376cd2d97526eeb6695 Author: David Carlier Date: Sat Mar 30 14:19:05 2024 +0000 std::thread: adding freebsd/netbsd to the linux's get_name implementation. commit ef493651025db2d5c38225c12ef97fd832c00c4a Merge: 18527282244 16d11c539f9 Author: bors Date: Sat Mar 30 15:53:28 2024 +0000 Auto merge of #123207 - Urgau:improve_ambi_non_null, r=Nadrieril Add support for `NonNull`s in the `ambiguous_wide_ptr_comparisions` lint This PR add support for `NonNull` pointers in the `ambiguous_wide_ptr_comparisions` lint. Fixes https://github.com/rust-lang/rust/issues/121264 r? `@Nadrieril` (since you just reviewed #121268, feel free to reassign) commit f04352a7dd010c0c37c835cd1b72c402460dffe3 Merge: d3accba79b4 a8b0f6f239a Author: bors Date: Sat Mar 30 15:45:01 2024 +0000 Auto merge of #3430 - RalfJung:doc, r=RalfJung make some doc comments not doc tests `./miri test --doc` will run doctests even if we have them disabled (that's a cargo quirk: https://github.com/rust-lang/cargo/issues/13668). This fixes that command to not fail. commit a8b0f6f239aed52824682c42a3753687483a2607 Author: Ralf Jung Date: Sat Mar 30 16:42:33 2024 +0100 make some doc comments not doc tests commit e1b8441899b87e52d0b368f55fc921ec668cde1f Author: Nadrieril Date: Sat Mar 30 16:18:18 2024 +0100 Require enum indices to be contiguous commit bda301ead8ab19814853dd876dbf93c09e7a4282 Author: Michael Goulet Date: Fri Mar 29 19:50:08 2024 -0400 Stop calling visitors V commit 71ea506d3df77b77a38b6f1ad0c7926c08b37f32 Author: klensy Date: Sat Mar 30 17:39:43 2024 +0300 bump tracing-tree to 0.3 Only change is https://github.com/davidbarsky/tracing-tree/pull/76 dedupes tracing-log dupes nu-ansi-term commit 18527282244431f597720e4fd42199e7333cc1c1 Merge: 40116ad1ede 9b067df6188 Author: bors Date: Sat Mar 30 13:51:24 2024 +0000 Auto merge of #123230 - matthiaskrgr:rollup-4twuzj4, r=matthiaskrgr Rollup of 5 pull requests Successful merges: - #121573 (unix_sigpipe: Add test for SIGPIPE disposition in child processes) - #123170 (Replace regions in const canonical vars' types with `'static` in next-solver canonicalizer) - #123200 (KCFI: Require -C panic=abort) - #123201 (Improve wording in std::any explanation) - #123224 (compiletest: print reason for failing to read tests) r? `@ghost` `@rustbot` modify labels: rollup commit 9b067df6188f6b0f062c5e2931e47dd0d67b541a Merge: 558880ab882 b20e267cc52 Author: Matthias Krüger Date: Sat Mar 30 14:30:51 2024 +0100 Rollup merge of #123224 - pacak:better-error-message, r=compiler-errors compiletest: print reason for failing to read tests Turns this ``` Could not read tests from /path/to/rust/tests/run-make note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace Build completed unsuccessfully in 0:00:05 ``` into this: ``` Could not read tests from /path/to/rust/tests/run-make: run-make tests cannot have both `Makefile` and `rmake.rs` note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace Build completed unsuccessfully in 0:00:05 ``` While first one is technically correct - it's not helpful at all, adding backtrace is not making it any better. commit 558880ab882a97e2a611a8669f9d9f43f23c529e Merge: 3afd111489f 7804edebfec Author: Matthias Krüger Date: Sat Mar 30 14:30:50 2024 +0100 Rollup merge of #123201 - Wilfred:patch-2, r=Nilstrieb Improve wording in std::any explanation Prefer 'log' over 'log out' to avoid confusion, and use backticks consistently. commit 3afd111489f619659669ef0a6013a049d5e69a84 Merge: 93f14432800 29c1a2b9e90 Author: Matthias Krüger Date: Sat Mar 30 14:30:50 2024 +0100 Rollup merge of #123200 - maurer:kcfi-abort, r=compiler-errors KCFI: Require -C panic=abort While the KCFI scheme is not incompatible with unwinding, LLVM's `invoke` instruction does not currently support KCFI bundles. While it likely will in the near future, we won't be able to assume that in Rust for a while. We encountered this problem while [turning on closure support](https://github.com/rust-lang/rust/pull/123106#issuecomment-2027436640). r? ``@workingjubilee`` commit 93f14432800fc2dc0a796446a5ed66b80fbca9e0 Merge: 5977d63925e 6439c7fe238 Author: Matthias Krüger Date: Sat Mar 30 14:30:49 2024 +0100 Rollup merge of #123170 - compiler-errors:const-statics, r=lcnr Replace regions in const canonical vars' types with `'static` in next-solver canonicalizer We shouldn't ever have non-static regions in consts on stable (or really any regions at all, lol). The test I committed is less minimal than, e.g., https://github.com/rust-lang/rust/issues/123155?notification_referrer_id=NT_kwDOADgQyrMxMDAzNDU4MDI0OTozNjc0MzE0#issuecomment-2025472029 -- however, I believe that it actually portrays the underlying issue here a bit better than that one. In the linked issue, we end up emitting a normalizes-to predicate for a const placeholder because we don't actually unify `false` and `""`. In the test I committed, we emit a normalizes-to predicate as a part of actually solving a negative coherence goal. Fixes #123155 Fixes #118783 r? lcnr commit 5977d63925e31d6d706fc6bf5f09ee92fb46213a Merge: 7e0ed43287a 71eb763d23c Author: Matthias Krüger Date: Sat Mar 30 14:30:49 2024 +0100 Rollup merge of #121573 - Enselic:sigpipe-child-process, r=Mark-Simulacrum unix_sigpipe: Add test for SIGPIPE disposition in child processes To make it clearer what the impact would be to stop using `SIG_IGN` and instead use a noop handler, like suggested [here](https://github.com/rust-lang/rust/issues/62569#issuecomment-1961586025) and implemented [here](https://github.com/rust-lang/rust/pull/121578). Part of https://github.com/rust-lang/rust/issues/97889 commit e0e7ee183f673b13ba9378450db5175fe2f257b7 Merge: 88d842ed298 91f3fad8e73 Author: bors Date: Sat Mar 30 13:21:20 2024 +0000 Auto merge of #12563 - J-ZhengLi:issue11513, r=Alexendoo make sure checked type implements `Try` trait when linting [`question_mark`] (indirectly) fixes: #12412 and fixes: #11983 --- changelog: make sure checked type implements `Try` trait when linting [`question_mark`] commit 3855b8bb609bbaaa3871797b40a78757f88d9b12 Author: George Bateman Date: Tue Jul 25 22:27:15 2023 +0100 Make {integer}::from_str_radix constant commit 88d842ed298094a5f2c998ca1d6e6d2961d985a6 Merge: 971e4355b9e 5750e4670b7 Author: bors Date: Sat Mar 30 12:32:38 2024 +0000 Auto merge of #12579 - J-ZhengLi:issue12569, r=Alexendoo fix [`manual_unwrap_or_default`] suggestion ignoring side-effects fixes: #12569 closes: #12580 change applicability to `MaybeIncorrect` base on suggestion in [this zulip discussion](https://rust-lang.zulipchat.com/#narrow/stream/257328-clippy/topic/.60manual_unwrap_or_default.60.20suggestion.20removes.20comments) --- changelog: fix [`manual_unwrap_or_default`] suggestion ignoring side-effects, and adjust its applicability. commit 40116ad1edef3ee66006e95fa9a483940aaa501c Merge: 7e0ed43287a f487d833908 Author: bors Date: Sat Mar 30 11:48:31 2024 +0000 Auto merge of #123214 - compiler-errors:subst, r=estebank Assert that ADTs have the right number of args We're doing it for many other types, let's also do ADTs 😇 commit e7829154b6dbfd0d5881fd6ce09a61108047e159 Author: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat Mar 30 11:02:18 2024 +0000 Rustup to rustc 1.79.0-nightly (faae5f1ff 2024-03-29) commit 362caf7a7058504f0af60d1bb8f47af40627666a Merge: 29b2d426b5d b2f6349b49d Author: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat Mar 30 10:50:14 2024 +0000 Sync from rust faae5f1ffe7b7b97e91eee6184807ac7307aafaa commit 0601f0c66d4ea250193a64a214988da425c3a47d Author: Scott McMurray Date: Sat Mar 30 00:36:45 2024 -0700 De-LLVM the unchecked shifts [MCP#693] This is just one part of the MCP, but it's the one that IMHO removes the most noise from the standard library code. Seems net simpler this way, since MIR already supported heterogeneous shifts anyway, and thus it's not more work for backends than before. commit 7e0ed43287a658be61dab00b68827865f2fc9c5a Merge: 50e3d6296d6 d54e9833e35 Author: bors Date: Sat Mar 30 08:38:48 2024 +0000 Auto merge of #123202 - estebank:issue-123009, r=compiler-errors Do not attempt to write `ty::Err` on binding that isn't from current HIR Owner Fix #123009. Follow up to #122119. commit d3accba79b4f700527b6a0e0d05a78127fccf694 Merge: eae940fcef7 28521fd2eb4 Author: bors Date: Sat Mar 30 08:29:08 2024 +0000 Auto merge of #3428 - rust-lang:rustup-2024-03-30, r=RalfJung Automatic Rustup commit 50e3d6296d6e7c1d0f4e85f674139c8463f62934 Merge: 174d07b539e d301f40c84c Author: bors Date: Sat Mar 30 06:38:18 2024 +0000 Auto merge of #123012 - maurer:cfi-supertraits, r=compiler-errors CFI: Support calling methods on supertraits Automatically adjust `Virtual` calls to supertrait functions to use the supertrait's trait object type as the receiver rather than the child trait. cc `@compiler-errors` - this is the next usage of `trait_object_ty` I intend to have, so I thought it might be relevant while reviewing the existing one. commit 28521fd2eb40339a20e10c124e60b5efbc31e17b Merge: fee9a8eab8f 69fa40cb483 Author: The Miri Cronjob Bot Date: Sat Mar 30 05:23:38 2024 +0000 Merge from rustc commit fee9a8eab8f1ae85135f2de7bf8bee815e364665 Author: The Miri Cronjob Bot Date: Sat Mar 30 05:16:27 2024 +0000 Preparing for merge from rustc commit 174d07b539eaafed82f098d5ec86713312aa02c3 Merge: 69fa40cb483 ea92faec491 Author: bors Date: Sat Mar 30 04:36:09 2024 +0000 Auto merge of #121948 - Gankra:stab-align, r=dtolnay stabilize ptr.is_aligned, move ptr.is_aligned_to to a new feature gate This is an alternative to #121920 commit b20e267cc52d495a7617c57006644730d1ba60dc Author: Michael Baikov Date: Fri Mar 29 23:00:54 2024 -0400 compiletest: print reason for failing to read tests commit 69fa40cb48384fad7930dce2d9a20d18fe4d1b51 Merge: 877d36b1928 41e97a0a3f5 Author: bors Date: Sat Mar 30 02:31:55 2024 +0000 Auto merge of #120557 - n8henrie:issue_120553, r=Mark-Simulacrum Add rust-lldb pretty printing for Path and PathBuf Fixes https://github.com/rust-lang/rust/issues/120553 Fixes https://github.com/rust-lang/rust/issues/48462 commit f487d8339086eb3bf0c0aec5fac7d343c5d8fa46 Author: Michael Goulet Date: Fri Mar 29 22:23:10 2024 -0400 Stop doing so much to handle subdiagnostics commit bc1f1ef2c82c5c84e0d90c5c5d261444fba9ffec Author: Michael Goulet Date: Fri Mar 29 21:21:41 2024 -0400 Stop removing substs from Adt type in coherence commit 5750e4670b7e217bb1c6260bf54dfcc0ef2e76cb Author: J-ZhengLi Date: Sat Mar 30 09:18:53 2024 +0800 fix [`manual_unwrap_or_default`] suggestion ignoring side-effects commit 5e2c54977202a0a245dca4bfd5171ced47bed487 Author: Michael Goulet Date: Fri Mar 29 20:40:45 2024 -0400 Assert that ADTs have the right number of substs commit 877d36b1928b5a4f7d193517b48290ecbe404d71 Merge: a3cfa031fa7 4500c83c62e Author: bors Date: Sat Mar 30 00:29:24 2024 +0000 Auto merge of #122976 - caibear:optimize_reserve_for_push, r=cuviper Remove len argument from RawVec::reserve_for_push Removes `RawVec::reserve_for_push`'s `len` argument since it's always the same as capacity. Also makes `Vec::insert` use `RawVec::reserve_for_push`. commit 37be3e4dd5ec4aa11959f9fd24d1c1dc53da44bd Author: y21 <30553356+y21@users.noreply.github.com> Date: Fri Aug 18 09:25:18 2023 +0200 [`type_id_on_box`]: lint of `Any` subtraits commit 41e97a0a3f5c60c24547c849cee03681983b8471 Author: Nathan Henrie Date: Thu Feb 1 09:08:52 2024 -0700 Add rust-lldb pretty printing for Path and PathBuf Fixes https://github.com/rust-lang/rust/issues/120553 Fixes https://github.com/rust-lang/rust/issues/48462 commit ea92faec491084849f9c2fb258a0e3161bb29ae4 Author: Aria Beingessner Date: Sun Mar 3 14:44:15 2024 -0500 stabilize ptr.is_aligned, move ptr.is_aligned_to to a new feature gate This is an alternative to #121920 commit 4500c83c62e9fe90b9807006bcd809af1ec940b3 Author: Cai Bear Date: Fri Mar 29 15:37:43 2024 -0700 Fix test. commit a3cfa031fa7726a957d73e6cad5744eb9706f56d Merge: faae5f1ffe7 fe8dc2a3ea4 Author: bors Date: Fri Mar 29 22:26:56 2024 +0000 Auto merge of #123208 - weihanglo:update-cargo, r=weihanglo Update cargo 8 commits in 499a61ce7a0fc6a72040084862a68b2603e770e8..a59aba136aab5510c16b0750a36cbd9916f91796 2024-03-26 04:17:04 +0000 to 2024-03-28 21:21:41 +0000 - refactor(package): Simplify getting of published Manifest (rust-lang/cargo#13666) - fix(toml): Warn on unused workspace.dependencies keys on virtual workspaces (rust-lang/cargo#13664) - docs: clarify `--locked` ensures Cargo uses dependency versions in lockfile (rust-lang/cargo#13665) - RUSTC_WORKSPACE_WRAPPER: clarify docs (rust-lang/cargo#13648) - fix(add): Preserve comments when updating simple deps (rust-lang/cargo#13655) - fix(generate-lockfile): hold lock before querying index (rust-lang/cargo#13657) - test: Add asserts to catch BorrowMutError's (rust-lang/cargo#13651) - Publish test crates (rust-lang/cargo#13418) r? ghost commit fe8dc2a3ea471bbec6fb03abff813f488470f3df Author: Weihang Lo Date: Fri Mar 29 17:56:19 2024 -0400 Update cargo commit 4f82731bba9bebf452e3ffae383ab5d57522562d Author: Justin Karneges Date: Fri Mar 29 14:27:13 2024 -0700 update tests commit 16d11c539f9783a5e066d90a7d7b17f70ee2b086 Author: Urgau Date: Mon Feb 19 21:50:33 2024 +0100 Add support for NonNull in ambiguous_wide_ptr_comparisions commit faae5f1ffe7b7b97e91eee6184807ac7307aafaa Merge: af4a5a13a15 50392ccc5a3 Author: bors Date: Fri Mar 29 20:25:08 2024 +0000 Auto merge of #122520 - scottmcm:stabilize_unchecked_math_basics, r=jhpratt Stabilize `unchecked_{add,sub,mul}` Tracking issue: #85122 I think we might as well just stabilize these basic three. They're the ones that have `nuw`/`nsw` flags in LLVM. Notably, this doesn't include the potentially-more-complex or -more-situational things like `unchecked_neg` or `unchecked_shr` that are under different feature flags. To quote Ralf https://github.com/rust-lang/rust/issues/85122#issuecomment-1681669646, > Are there any objections to stabilizing at least `unchecked_{add,sub,mul}`? For those there shouldn't be any surprises about what their safety requirements are. *Semantially* these are [already available on stable, even in `const`, via](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=bdb1ff889b61950897f1e9f56d0c9a36) `checked_*`+`unreachable_unchecked`. So IMHO we might as well just let people write them directly, rather than try to go through a `let Some(x) = x.checked_add(y) else { unsafe { hint::unreachable_unchecked() }};` dance. I added additional text to each method to attempt to better describe the behaviour and encourage `wrapping_*` instead. r? rust-lang/libs-api commit 971e4355b9e986bcba7c974d37f0b4dec5854f69 Merge: d928657d9df 89588f41f8a Author: bors Date: Fri Mar 29 19:34:44 2024 +0000 Auto merge of #12543 - Xaeroxe:manual-clamp-const, r=xFrednet,GuillaumeGomez restrict manual_clamp to const case, bring it out of nursery Implements the plan that I described in https://github.com/rust-lang/rust-clippy/pull/9484#issuecomment-1374522054 This does two things primarily 1. Restrict `manual_clamp` such that it will only trigger if we are able to guarantee that `clamp` won't panic at runtime. 2. Bring `manual_clamp` out of nursery status and move it into the complexity group. changelog: [`manual_clamp`]: Restrict this lint such that it only triggers if max and min are const, and max is greater than or equal to min. Then bring it out of the nursery group. commit d928657d9df509f9415ae7cb37b7d8be98022283 Merge: 97ba291d5aa 7aac504e416 Author: bors Date: Fri Mar 29 19:24:01 2024 +0000 Auto merge of #12587 - shandongbinzhou:master, r=Jarcho Fix typo in comment Thank you for making Clippy better! We're collecting our changelog from pull request descriptions. If your PR only includes internal changes, you can just write `changelog: none`. Otherwise, please write a short comment explaining your change. It's also helpful for us that the lint name is put within backticks (`` ` ` ``), and then encapsulated by square brackets (`[]`), for example: ``` changelog: [`lint_name`]: your change ``` If your PR fixes an issue, you can add `fixes #issue_number` into this PR description. This way the issue will be automatically closed when your PR is merged. If you added a new lint, here's a checklist for things that will be checked during review or continuous integration. - \[x] Followed [lint naming conventions][lint_naming] - \[ ] Added passing UI tests (including committed `.stderr` file) - \[ ] `cargo test` passes locally - \[ ] Executed `cargo dev update_lints` - \[ ] Added lint documentation - \[x] Run `cargo dev fmt` [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints Note that you can skip the above if you are just opening a WIP PR in order to get feedback. Delete this line and everything above before opening your PR. --- *Please write a short comment explaining your change (or "none" for internal only changes)* changelog: None commit 13838a53fd736526626993b842562322eec24c4e Author: Justin Karneges Date: Fri Mar 29 12:16:09 2024 -0700 rustfmt commit d54e9833e3513958a9e03c5f50b9496c618ebde5 Author: Esteban Küber Date: Fri Mar 29 19:05:54 2024 +0000 Do not attempt to write `ty::Err` on binding that isn't from current HIR Owner Fix #123009. commit af4a5a13a15fa0c60e06321077ef452f769b42fd Merge: 399fa2f6e41 d4b514f982e Author: bors Date: Fri Mar 29 18:23:57 2024 +0000 Auto merge of #121268 - Urgau:improve_ambi_wide_ptr_cmps, r=Nadrieril Add detection of [Partial]Ord methods in the `ambiguous_wide_pointer_comparisons` lint Partially addresses https://github.com/rust-lang/rust/issues/121264 by adding diagnostics items for PartialOrd and Ord methods, detecting such diagnostics items as "binary operation" and suggesting the correct replacement. I also took the opportunity to change the suggestion to use new methods `.cast()` on `*mut T` an d `*const T`. commit d301f40c84c0a65ee7b86a6cdcca3544ab5db0fe Author: Matthew Maurer Date: Sun Mar 24 18:56:15 2024 +0000 CFI: Encode Virtual calls as calls through the defining trait For example, if `trait Foo: Bar`, and we try to call a method from `Bar` on `dyn Foo`, encode the callsite as passing a `dyn Bar`, not a `dyn Foo`. commit c6ac3b02dbd74a4116f7198c442f419fee3cacc5 Author: Justin Karneges Date: Thu Mar 28 09:55:07 2024 -0700 Add `Context::ext` commit 7804edebfec17dec75bd6d2bb67e641434fbaa27 Author: Wilfred Hughes Date: Fri Mar 29 10:10:52 2024 -0700 Improve wording in std::any explanation Prefer 'log' over 'log out' to avoid confusion, and use backticks consistently. commit f5a9250147f6569d8d89334dc9cca79c0322729f Merge: 9d84142ef22 34cde2cebc6 Author: bors Date: Fri Mar 29 17:01:51 2024 +0000 Auto merge of #16975 - HKalbasi:test-explorer, r=HKalbasi Prompt the user to reload the window when enabling test explorer commit 29c1a2b9e9009287025d620ad6897d1be4897923 Author: Matthew Maurer Date: Fri Mar 29 16:35:58 2024 +0000 KCFI: Require -C panic=abort While the KCFI scheme is not incompatible with unwinding, LLVM's `invoke` instruction does not currently support KCFI bundles. While it likely will in the near future, we won't be able to assume that in Rust for a while. commit 54ab4258397030d71cbc5fbf279c0bc1861560aa Author: Arthur Carcano Date: Fri Mar 29 17:09:34 2024 +0100 Add fn const BuildHasherDefault::new Because `HashMap::with_hasher` constness is being stabilized this will in turn allow creating empty HashMap> in const context for any H: Default + Hasher. commit 399fa2f6e419fd2c70942c191c2e55814af8d167 Merge: 685927aae69 8d820c0c47e Author: bors Date: Fri Mar 29 16:02:04 2024 +0000 Auto merge of #123194 - matthiaskrgr:rollup-vhdc8hw, r=matthiaskrgr Rollup of 4 pull requests Successful merges: - #123176 (Normalize the result of `Fields::ty_with_args`) - #123186 (copy any file from stage0/lib to stage0-sysroot/lib) - #123187 (Forward port 1.77.1 release notes) - #123188 (compiler: fix few unused_peekable and needless_pass_by_ref_mut clippy lints) r? `@ghost` `@rustbot` modify labels: rollup commit 89588f41f8a96984601b0abc7f8eaacfa3b7da8f Author: Jacob Kiesel Date: Fri Mar 29 09:38:59 2024 -0600 Add limitations section, move check commit d4b514f982e4214e0f9237c905670b1207ae0c95 Author: Urgau Date: Sun Feb 18 15:23:03 2024 +0100 Add detection of [Partial]Ord methods to the ambiguous wide ptr cmp lint commit 4a9f3cac887023d0230729e898240b85508aa791 Author: Urgau Date: Sun Feb 18 15:21:44 2024 +0100 Add diagnostic items for Ord and PartialOrd methods commit 97ba291d5aa026353ad93e48cf00e06f08c73830 Merge: 124e68bef8b 01646457a92 Author: bors Date: Fri Mar 29 15:07:03 2024 +0000 Auto merge of #12582 - kpreid:stacksize, r=Manishearth `large_stack_frames`: print total size and largest component. Instead of just saying “this function's stack frame is big”, report: * the (presumed) size of the frame * the size and type of the largest local contributing to that size * the configurable limit that was exceeded (once) Known issues: * The lint may report an over-estimate because codegen may be able to overlap some of these locals. However, that already affected whether the lint fired at all; I believe this change is still an improvement because it gives the user much more actionable information about _why_ the lint fired. * Please tell me a better way to determine whether a local has a variable name. changelog: [`large_stack_frames`]: print total size and largest component. commit 34cde2cebc67b0b99b5c738f948497691d0ed5e3 Author: hkalbasi Date: Fri Mar 29 18:08:16 2024 +0330 Prompt the user to reload the window when enabling test explorer commit 8d820c0c47ed2be6897859c4eeb7679e4c083ac4 Merge: a18da0088e8 8245718503f Author: Matthias Krüger Date: Fri Mar 29 15:17:11 2024 +0100 Rollup merge of #123188 - klensy:clippy-me2, r=Nilstrieb compiler: fix few unused_peekable and needless_pass_by_ref_mut clippy lints This fixes few instances of `unused_peekable` and `needless_pass_by_ref_mut`. While i expected to fix more warnings, `needless_pass_by_ref_mut` produced too much for one PR, so i stopped here. Better reviewed commit by commit, as fixes splitted by chunks. commit a18da0088e8f1dbefd55e8974d8eae45c8bce80d Merge: b48411bcd44 5eb78c515c7 Author: Matthias Krüger Date: Fri Mar 29 15:17:11 2024 +0100 Rollup merge of #123187 - yedayak:relnotes-1.77.1, r=Mark-Simulacrum Forward port 1.77.1 release notes commit b48411bcd440b49b5af2446bc3c7a0de17cc4556 Merge: 73a42086384 5fe364afdd5 Author: Matthias Krüger Date: Fri Mar 29 15:17:10 2024 +0100 Rollup merge of #123186 - onur-ozkan:llvm-library-bug, r=Kobzol copy any file from stage0/lib to stage0-sysroot/lib With the LLVM 18 upgrade, the name of the LLVM library has been changed to something like `libLLVM.so.18.1-rust-1.78.0-beta`, which `is_dylib` function cannot determine as it only looks whether files are ending with ".so" or not. This change resolves this problem by no longer doing that ".so" check, as we need all files from the stage0/lib as they are all dependency of rustc anyway. Fixes #122913 commit 73a42086384028676868f610252bcc71ed946f43 Merge: 45796d1c244 a325bce3cd1 Author: Matthias Krüger Date: Fri Mar 29 15:17:10 2024 +0100 Rollup merge of #123176 - celinval:smir-field-ty, r=oli-obk Normalize the result of `Fields::ty_with_args` We were only instantiating before, which would leak an AliasTy. I added a test case that reproduce the issue seen here: https://github.com/model-checking/kani/issues/3113 r? ``@oli-obk`` commit b2f6349b49ddf9211096097cde77642396336962 Merge: e8c13080f7c 4d7ded634aa Author: bors Date: Fri Mar 29 14:00:21 2024 +0000 Auto merge of #122450 - Urgau:simplify-trim-paths-feature, r=michaelwoerister Simplify trim-paths feature by merging all debuginfo options together This PR simplifies the trim-paths feature by merging all debuginfo options together, as described in https://github.com/rust-lang/rust/issues/111540#issuecomment-1994010274. And also do some correctness fixes found during the review. cc `@weihanglo` r? `@michaelwoerister` commit 685927aae69657b46323cffbeb0062835bd7fa2b Merge: 45796d1c244 fefb8f1f9cb Author: bors Date: Fri Mar 29 14:00:21 2024 +0000 Auto merge of #122450 - Urgau:simplify-trim-paths-feature, r=michaelwoerister Simplify trim-paths feature by merging all debuginfo options together This PR simplifies the trim-paths feature by merging all debuginfo options together, as described in https://github.com/rust-lang/rust/issues/111540#issuecomment-1994010274. And also do some correctness fixes found during the review. cc `@weihanglo` r? `@michaelwoerister` commit 9d84142ef22103b76262974301f2cb22c186f0fe Merge: a8b7acf22ff 69c4ac6304c Author: bors Date: Fri Mar 29 13:15:08 2024 +0000 Auto merge of #16974 - dfireBird:generic_params_refactor, r=lnicola refactor: Implement len and is_empty method in generic_params commit 69c4ac6304ca2b56436064cf0b933637ab778eed Author: dfireBird Date: Fri Mar 29 18:26:46 2024 +0530 implement len and is_empty method in generic_params commit 45796d1c24445b298567752519471cef2cff3298 Merge: 58dcd1fdb9a e9315959099 Author: bors Date: Fri Mar 29 11:08:11 2024 +0000 Auto merge of #123080 - Jules-Bertholet:mut-ref-mut, r=Nadrieril Match ergonomics 2024: implement mutable by-reference bindings Implements the mutable by-reference bindings portion of match ergonomics 2024 (#123076), with the `mut ref`/`mut ref mut` syntax, under feature gate `mut_ref`. r? `@Nadrieril` `@rustbot` label A-patterns A-edition-2024 commit 58dcd1fdb9a605bd2f0126d1b06ae6511bbc1dab Merge: 1c195955759 8e6b4e91b68 Author: bors Date: Fri Mar 29 09:04:05 2024 +0000 Auto merge of #123071 - rcvalle:rust-cfi-fix-method-fn-ptr-cast, r=compiler-errors CFI: Fix methods as function pointer cast Fix casting between methods and function pointers by assigning a secondary type id to methods with their concrete self so they can be used as function pointers. This was split off from #116404. cc `@compiler-errors` `@workingjubilee` commit a4087b7915b2b77ecb198f0f2b1afd68a24a3144 Author: Jakub Beránek Date: Fri Mar 29 09:36:56 2024 +0100 Log BOLT args in bootstrap `rustc` shim commit 7aac504e4160c9524412af5f83eeb111918cc6ab Author: shandongbinzhou Date: Fri Mar 29 16:17:07 2024 +0800 Fix typo in comment Signed-off-by: shandongbinzhou commit 5eb78c515c777c232c35b2e606bcec7cefe875af Author: Yedaya Katsman Date: Fri Mar 29 10:44:19 2024 +0300 Forward port 1.77.1 release notes commit 5fe364afdd590c8f928721820e490f82b90f65e0 Author: onur-ozkan Date: Fri Mar 29 10:06:13 2024 +0300 copy any file from stage0/lib to stage0-sysroot/lib With the LLVM 18 upgrade, the name of the LLVM library has been changed to something like `libLLVM.so.18.1-rust-1.78.0-beta`, which `is_dylib` function cannot determine as it only looks whether files are ending with ".so" or not. This change resolves this problem by no longer doing that ".so" check, as we need all files from the stage0/lib as they are all dependency of rustc anyway. Signed-off-by: onur-ozkan commit 1c19595575968ea77c7f85e97c67d44d8c0f9a68 Merge: 760e567af53 2f1ab2ce092 Author: bors Date: Fri Mar 29 07:02:56 2024 +0000 Auto merge of #122616 - Jules-Bertholet:casemappingiter-layout, r=Nilstrieb Optimize `core::char::CaseMappingIter` Godbolt says this saves a few instructions… `@rustbot` label T-libs A-layout C-optimization commit eae940fcef7f95d31b566f92f511f91f24f2f703 Merge: 58a771ebfa2 ed29546a27d Author: bors Date: Fri Mar 29 06:16:56 2024 +0000 Auto merge of #3427 - rust-lang:rustup-2024-03-29, r=saethlin Automatic Rustup commit ed29546a27db9fa024c6f99a7dd001e86d75e14f Merge: 29a59beaa6b 5eb78c515c7 Author: The Miri Cronjob Bot Date: Fri Mar 29 05:02:09 2024 +0000 Merge from rustc commit 29a59beaa6bc7841b12d6523bd638dbd6e7d0b01 Author: The Miri Cronjob Bot Date: Fri Mar 29 04:54:51 2024 +0000 Preparing for merge from rustc commit b110cb3dc6da68fc46b08d7b0ed40a48b3503306 Author: Stepan Koltsov Date: Fri Mar 29 03:44:16 2024 +0000 Require Debug for Pointee::Metadata Useful for debugging commit 760e567af5398a0d8c512f904e551e1f38e00d79 Merge: db2f9759f43 47ed73a7b51 Author: bors Date: Fri Mar 29 02:25:43 2024 +0000 Auto merge of #122975 - DianQK:simplify_ub_check, r=saethlin Eliminate `UbChecks` for non-standard libraries The purpose of this PR is to allow other passes to treat `UbChecks` as constants in MIR for optimization after #122629. r? RalfJung commit a8b7acf22ff5f4e9d075dfa45ddaacf7b608e35e Merge: ab10eea62ec beec6914c84 Author: bors Date: Fri Mar 29 02:06:22 2024 +0000 Auto merge of #16971 - HKalbasi:test-explorer, r=HKalbasi Resolve tests per file instead of per crate in test explorer Fix part of #16827 commit beec6914c84b1f7459847d0226086759e63b3f2c Author: hkalbasi Date: Fri Mar 29 05:34:43 2024 +0330 Resolve tests per file instead of per crate in test explorer commit e8c13080f7c7ead74f59bf48b7985793fedf785d Merge: c5c31447a7f 05783c8ed68 Author: bors Date: Fri Mar 29 00:24:01 2024 +0000 Auto merge of #122671 - Mark-Simulacrum:const-panic-msg, r=Nilstrieb Codegen const panic messages as function calls This skips emitting extra arguments at every callsite (of which there can be many). For a librustc_driver build with overflow checks enabled, this cuts 0.7MB from the resulting shared library (see [perf]). A sample improvement from nightly: ``` leaq str.0(%rip), %rdi leaq .Lalloc_d6aeb8e2aa19de39a7f0e861c998af13(%rip), %rdx movl $25, %esi callq *_ZN4core9panicking5panic17h17cabb89c5bcc999E@GOTPCREL(%rip) ``` to this PR: ``` leaq .Lalloc_d6aeb8e2aa19de39a7f0e861c998af13(%rip), %rdi callq *_RNvNtNtCsduqIKoij8JB_4core9panicking11panic_const23panic_const_div_by_zero@GOTPCREL(%rip) ``` [perf]: https://perf.rust-lang.org/compare.html?start=a7e4de13c1785819f4d61da41f6704ed69d5f203&end=64fbb4f0b2d621ff46d559d1e9f5ad89a8d7789b&stat=instructions:u commit db2f9759f43167755d4eebb0a1358df9766a505e Merge: d74804636fa 00f4daa2767 Author: bors Date: Fri Mar 29 00:24:01 2024 +0000 Auto merge of #122671 - Mark-Simulacrum:const-panic-msg, r=Nilstrieb Codegen const panic messages as function calls This skips emitting extra arguments at every callsite (of which there can be many). For a librustc_driver build with overflow checks enabled, this cuts 0.7MB from the resulting shared library (see [perf]). A sample improvement from nightly: ``` leaq str.0(%rip), %rdi leaq .Lalloc_d6aeb8e2aa19de39a7f0e861c998af13(%rip), %rdx movl $25, %esi callq *_ZN4core9panicking5panic17h17cabb89c5bcc999E@GOTPCREL(%rip) ``` to this PR: ``` leaq .Lalloc_d6aeb8e2aa19de39a7f0e861c998af13(%rip), %rdi callq *_RNvNtNtCsduqIKoij8JB_4core9panicking11panic_const23panic_const_div_by_zero@GOTPCREL(%rip) ``` [perf]: https://perf.rust-lang.org/compare.html?start=a7e4de13c1785819f4d61da41f6704ed69d5f203&end=64fbb4f0b2d621ff46d559d1e9f5ad89a8d7789b&stat=instructions:u commit aba592d09c78fcf1432de24c200fffa2fb21aeb6 Author: Cai Bear Date: Thu Mar 28 16:38:01 2024 -0700 Rename reserve_for_push to grow_one and fix comment. commit 78dc89b0d5654bb16a63121c60198cbe24878289 Author: Cai Bear Date: Sat Mar 23 21:45:49 2024 -0700 Fix previous. commit 18d390883efc7eaf5ced77d89abc5b51c8b7f816 Author: Cai Bear Date: Sat Mar 23 21:27:59 2024 -0700 Remove len argument from RawVec::reserve_for_push because it's always equal to capacity. Also make Vec::insert use reserve_for_push. commit fd7909aa59a761f8ba5bf34df3d84a6afa4bff1c Author: Ralf Jung Date: Thu Mar 28 22:46:07 2024 +0100 x.py test miri: respect --no-doc / --doc commit a325bce3cd162a20ecab9ff31def3e6701c8ebea Author: Celina G. Val Date: Thu Mar 28 13:19:50 2024 -0700 Normalize the result of Fields::ty_with_args We were only instantiating before, which would leak an AliasTy. I added a test case that reproduce the issue seen here: https://github.com/model-checking/kani/issues/3113 commit d7d5fc97341ebc127ad716053c3efe6e613ba3a7 Author: Trevor Gross Date: Tue Mar 26 05:22:18 2024 -0400 Add basic trait impls for `f16` and `f128` Split off part of so the compiler doesn't ICE because it expects primitives to have some minimal traits. Fixes commit 01646457a9206429c9a463984d075de25805791e Author: Kevin Reid Date: Wed Mar 27 22:52:34 2024 -0700 `large_stack_frames`: print total size and largest component. Instead of just saying “this function's stack frame is big”, report: * the (presumed) size of the frame * the size and type of the largest local contributing to that size * the configurable limit that was exceeded (once) commit 4d7ded634aab19de52b382794010563574f1507d Author: Urgau Date: Fri Mar 22 15:27:17 2024 +0100 Replace Session should_remap_filepaths with filename_display_preference commit fefb8f1f9cb830e1ca0b0ba449db7c8e7d4ff7ba Author: Urgau Date: Fri Mar 22 15:27:17 2024 +0100 Replace Session should_remap_filepaths with filename_display_preference commit 6a2b2b43bd65ee7e3def7e10289bb98ca415dc03 Author: Urgau Date: Thu Mar 21 21:13:06 2024 +0100 Introduce `FileNameMapping::to_real_filename` and use it everywhere commit 4f4fa42b0ee1cf6c988d3f7ed6bcb4a51e788282 Author: Urgau Date: Thu Mar 21 21:13:06 2024 +0100 Introduce `FileNameMapping::to_real_filename` and use it everywhere commit d6a817a7d9b8950cf73914d17cfc1c8e35dc2c7f Author: Urgau Date: Tue Mar 19 13:51:22 2024 +0100 Make local_crate_source_file return a RealFileName so it can be remapped (or not) by callers commit ee2898d3f1cbece34153581823fafa7f572bbff0 Author: Urgau Date: Tue Mar 19 13:51:22 2024 +0100 Make local_crate_source_file return a RealFileName so it can be remapped (or not) by callers commit 5de668acb07218424cbfe79f32f4961ca773570d Author: Urgau Date: Tue Mar 19 13:51:22 2024 +0100 Replace `RemapFileNameExt::for_codegen` with explicit calls commit 106146fd958c3c0d3428cfc7be1f75c5bc81698f Author: Urgau Date: Tue Mar 19 13:51:22 2024 +0100 Replace `RemapFileNameExt::for_codegen` with explicit calls commit 777c6b46cc804a3bae345f9133d2e0a900026bdc Author: Urgau Date: Tue Mar 19 13:51:22 2024 +0100 Simplify trim-paths feature by merging all debuginfo options together commit ab10eea62ec59bc676a946ae8be562edcaccb950 Merge: 899db83128a 34a8cd6a7ab Author: bors Date: Thu Mar 28 16:45:45 2024 +0000 Auto merge of #16967 - dfireBird:fix-16963, r=lnicola fix: ADT hover considering only type or const len not lifetimes I feel like test doesn't do much. Please suggest if I should improve it? Fixes #16963 commit 6439c7fe238d14f10b9b4cfae328ec408e8dfde4 Author: Michael Goulet Date: Thu Mar 28 12:17:17 2024 -0400 Require foldability part of interner item bounds, remove redundant where clauses commit 08c7ff22645cbb661edd67a8a30cf3dd67a07c23 Author: Michael Goulet Date: Thu Mar 28 12:10:44 2024 -0400 Restrict const ty's regions to static when putting them in canonical var list commit 34a8cd6a7ab58d6d53d9c3af1fed2f4cc18a9e0e Author: dfireBird Date: Thu Mar 28 20:31:15 2024 +0530 fix ADT hover considering only type or const len not lifetimes commit 8245718503f24a0a316c6603195b94be23f4fda4 Author: klensy Date: Thu Mar 28 17:09:13 2024 +0300 and more warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_mir_transform\src\coroutine.rs:1229:11 | 1229 | body: &mut Body<'tcx>, | ^^^^^^^^^^^^^^^ help: consider changing to: `&Body<'tcx>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_mir_transform\src\nrvo.rs:123:11 | 123 | body: &mut mir::Body<'_>, | ^^^^^^^^^^^^^^^^^^ help: consider changing to: `&mir::Body<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_mir_transform\src\nrvo.rs:87:34 | 87 | fn local_eligible_for_nrvo(body: &mut mir::Body<'_>) -> Option { | ^^^^^^^^^^^^^^^^^^ help: consider changing to: `&mir::Body<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut commit 9a6b3dfc0636ab8eaece281ecfeceb2b2f393b06 Author: klensy Date: Thu Mar 28 16:57:32 2024 +0300 and few more maybe bug here? warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_borrowck\src\diagnostics\conflict_errors.rs:3857:35 | 3857 | pub(crate) fn emit(&self, cx: &mut MirBorrowckCtxt<'_, 'tcx>, diag: &mut Diag<'_>) -> String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&MirBorrowckCtxt<'_, 'tcx>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_borrowck\src\type_check\liveness\trace.rs:601:17 | 601 | typeck: &mut TypeChecker<'_, 'tcx>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&TypeChecker<'_, 'tcx>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut commit 5488e492af99e8a7b1b24cbccdfcd6635996220d Author: klensy Date: Thu Mar 28 16:26:37 2024 +0300 and few more warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_codegen_ssa\src\back\rpath.rs:80:41 | 80 | fn get_rpath_relative_to_output(config: &mut RPathConfig<'_>, lib: &Path) -> OsString { | ^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&RPathConfig<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_codegen_ssa\src\back\rpath.rs:76:42 | 76 | fn get_rpaths_relative_to_output(config: &mut RPathConfig<'_>) -> Vec { | ^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&RPathConfig<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_codegen_ssa\src\back\rpath.rs:55:23 | 55 | fn get_rpaths(config: &mut RPathConfig<'_>) -> Vec { | ^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&RPathConfig<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_codegen_ssa\src\back\rpath.rs:15:32 | 15 | pub fn get_rpath_flags(config: &mut RPathConfig<'_>) -> Vec { | ^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&RPathConfig<'_>` | = warning: changing this function will impact semver compatibility = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut commit 29b2d426b5d8d1a48b72e5d9ba60aa512dff928c Merge: 09fae60a86b c5c31447a7f Author: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu Mar 28 11:45:32 2024 +0000 Merge branch 'sync_from_rust' commit c5c31447a7f2d8ffc03f058e3d39cff7627d3eb1 Author: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu Mar 28 11:43:35 2024 +0000 Merge commit '09fae60a86b848a2fc0ad219ecc4e438dc1eef86' into sync_cg_clif-2024-03-28 commit c64a4403123c43b88f9261a055bda4b9fbd6fc73 Author: klensy Date: Thu Mar 28 13:16:22 2024 +0300 fix few more warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_trait_selection\src\traits\project.rs:511:12 | 511 | selcx: &mut SelectionContext<'a, 'tcx>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&SelectionContext<'a, 'tcx>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_trait_selection\src\traits\specialize\specialization_graph.rs:201:28 | 201 | fn iter_children(children: &mut Children) -> impl Iterator + '_ { | ^^^^^^^^^^^^^ help: consider changing to: `&Children` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut commit afd0a8eb8f2d26f540dd39b2b05739a7ecc0869d Author: klensy Date: Thu Mar 28 12:49:18 2024 +0300 change BuiltinDeriveFn type to get ExtCtxt by immutable ref and fix signatures commit 615bb53a8da597afb72a78b616cf0c747e81107e Author: klensy Date: Thu Mar 28 12:36:45 2024 +0300 compiler: fix few needless_pass_by_ref_mut clippy lints warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\clone.rs:160:9 | 160 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\cmp\partial_ord.rs:72:9 | 72 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\cmp\partial_eq.rs:19:18 | 19 | fn cs_eq(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr { | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\cmp\ord.rs:42:19 | 42 | pub fn cs_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr { | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\generic\mod.rs:917:13 | 917 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\generic\mod.rs:1406:13 | 1406 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\generic\mod.rs:1157:13 | 1157 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\generic\mod.rs:1103:13 | 1103 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\generic\mod.rs:1080:13 | 1080 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\generic\mod.rs:859:13 | 859 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\generic\mod.rs:805:13 | 805 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\generic\mod.rs:467:13 | 467 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\generic\mod.rs:457:13 | 457 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut commit ecb39926d86871ccec1e83d748452671a7202c38 Author: klensy Date: Thu Mar 28 12:16:08 2024 +0300 compiler: fix few needless_pass_by_ref_mut clippy lints warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\mod.rs:120:9 | 120 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\generic\mod.rs:1573:13 | 1573 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\generic\mod.rs:1556:13 | 1556 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\generic\mod.rs:1463:13 | 1463 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\generic\mod.rs:1433:36 | 1433 | fn summarise_struct(&self, cx: &mut ExtCtxt<'_>, struct_def: &VariantData) -> StaticFields { | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\generic\mod.rs:953:13 | 953 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\generic\mod.rs:932:13 | 932 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\generic\mod.rs:580:13 | 580 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\generic\mod.rs:989:13 | 989 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\clone.rs:97:9 | 97 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\cmp\eq.rs:52:9 | 52 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\hash.rs:50:9 | 50 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\encodable.rs:150:9 | 150 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\default.rs:176:9 | 176 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\default.rs:106:9 | 106 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\default.rs:57:9 | 57 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\default.rs:84:9 | 84 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\debug.rs:212:9 | 212 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\deriving\debug.rs:48:26 | 48 | fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr { | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut commit 316bc1c67cf72700a5e3c411fc044278eb1ef9ef Author: klensy Date: Thu Mar 28 12:04:00 2024 +0300 compiler: fix few needless_pass_by_ref_mut clippy lints warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\asm.rs:306:28 | 306 | fn err_duplicate_option(p: &mut Parser<'_>, symbol: Symbol, span: Span) { | ^^^^^^^^^^^^^^^ help: consider changing to: `&Parser<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\asm.rs:318:8 | 318 | p: &mut Parser<'a>, | ^^^^^^^^^^^^^^^ help: consider changing to: `&Parser<'a>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\assert.rs:114:25 | 114 | fn parse_assert<'a>(cx: &mut ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult<'a, Assert> { | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'a>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\asm.rs:32:10 | 32 | ecx: &mut ExtCtxt<'a>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'a>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\test.rs:99:9 | 99 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\source_util.rs:237:9 | 237 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\format.rs:809:10 | 809 | ecx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\format.rs:737:10 | 737 | ecx: &mut ExtCtxt<'a>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'a>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\format.rs:68:24 | 68 | fn parse_args<'a>(ecx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a, MacroInput> { | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'a>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\format.rs:607:10 | 607 | ecx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\edition_panic.rs:43:9 | 43 | cx: &'cx mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\concat_bytes.rs:11:9 | 11 | cx: &mut ExtCtxt<'_>, | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\cfg.rs:38:22 | 38 | fn parse_cfg<'a>(cx: &mut ExtCtxt<'a>, span: Span, tts: TokenStream) -> PResult<'a, ast::MetaItem> { | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'a>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_builtin_macros\src\cfg_accessible.rs:13:28 | 13 | fn validate_input<'a>(ecx: &mut ExtCtxt<'_>, mi: &'a ast::MetaItem) -> Option<&'a ast::Path> { | ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut commit 899db83128a151359bb1c58f6ede7f663635e2b1 Merge: 29a8e65bbe1 8f9a58c73d4 Author: bors Date: Thu Mar 28 08:57:29 2024 +0000 Auto merge of #16957 - poliorcetics:ab/push-tlzsqmqqurxs, r=lnicola fix: check for client support of relative glob patterns before using them Fixes #16955 commit aa355303195d9d6fecb016afb0d52f4cd7a52d09 Author: klensy Date: Thu Mar 28 11:30:49 2024 +0300 compiler: fix few needless_pass_by_ref_mut clippy lints warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_session\src\config.rs:2013:16 | 2013 | early_dcx: &mut EarlyDiagCtxt, | ^^^^^^^^^^^^^^^^^^ help: consider changing to: `&EarlyDiagCtxt` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_ast_passes\src\ast_validation.rs:1555:11 | 1555 | this: &mut AstValidator<'_>, | ^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&AstValidator<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_infer\src\infer\snapshot\fudge.rs:16:12 | 16 | table: &mut UnificationTable<'_, 'tcx, T>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&UnificationTable<'_, 'tcx, T>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_expand\src\expand.rs:961:13 | 961 | parser: &mut Parser<'a>, | ^^^^^^^^^^^^^^^ help: consider changing to: `&Parser<'a>` | = warning: changing this function will impact semver compatibility = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut commit b6cfe71f3891db6b7883fc31a8acc37c4f13c8ec Author: klensy Date: Thu Mar 28 11:03:48 2024 +0300 compiler: fix some clippy needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_session\src\config.rs:2111:20 | 2111 | unstable_opts: &mut UnstableOptions, | ^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&UnstableOptions` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut commit bf476417265d23e9a3371cb0901039b3b1335312 Author: klensy Date: Thu Mar 28 10:50:09 2024 +0300 compiler: fix unused_peekable clippy lint warning: `peek` never called on `Peekable` iterator --> compiler\rustc_session\src\utils.rs:130:13 | 130 | let mut args = std::env::args_os().map(|arg| arg.to_string_lossy().to_string()).peekable(); | ^^^^ | = help: consider removing the call to `peekable` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unused_peekable warning: `peek` never called on `Peekable` iterator --> compiler\rustc_trait_selection\src\traits\error_reporting\suggestions.rs:4934:17 | 4934 | let mut bounds = pred.bounds.iter().peekable(); | ^^^^^^ | = help: consider removing the call to `peekable` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unused_peekable commit 29a8e65bbe13359ade1e72a27e9acce6e095ee4a Merge: 4bf521df35d 35210899859 Author: bors Date: Thu Mar 28 06:49:49 2024 +0000 Auto merge of #16965 - roife:use-lldb-for-cpp-ext, r=lnicola fix: use lldb when debugging with C++ extension on MacOS See https://github.com/rust-lang/rust-analyzer/issues/16901#issuecomment-2024486941 This PR resolves the issue of being unable to debug using the C++ extension on macOS. By using special configurations for the `MIMode` on macOS, it enables the C++ extension to connect to lldb when debugging (without affecting other platforms). commit 3521089985951f684d33235dd0a5f99bbb3140f9 Author: roife Date: Thu Mar 28 14:25:26 2024 +0800 fix: use lldb when debugging with C++ extension commit 4bf521df35d7b52b44bf371b8ebb65d983d056d4 Merge: ad51a17c627 662ea73e4e2 Author: bors Date: Thu Mar 28 06:08:47 2024 +0000 Auto merge of #16964 - lnicola:debugger-order, r=roife fix: Revert debug extension priorities Close #16901 commit 662ea73e4e2df83de04c4f2936c3db9eb3a1e24c Author: Laurențiu Nicola Date: Thu Mar 28 07:57:53 2024 +0200 Revert debug extension priorities commit 124e68bef8be61aa151ff33bea325c832728146f Merge: 014230ce169 c27f52d6119 Author: bors Date: Thu Mar 28 02:00:07 2024 +0000 Auto merge of #12570 - J-ZhengLi:issue12569, r=Jarcho allow [`manual_unwrap_or_default`] in const function closes: #12568 --- changelog: allow [`manual_unwrap_or_default`] in const function This is a small fix, I was originally decided to fix it along with `#12568` but there are some problems needs to be addressed (which is why my branch is called `issue12569` 😆 ), so I decide to open a separated PR to fix them one at a time. commit 8e6b4e91b68a0921c534aa1174d27f3c6418b1e6 Author: Ramon de C Valle Date: Fri Mar 22 15:45:23 2024 -0700 CFI: Fix methods as function pointer cast Fix casting between methods and function pointers by assigning a secondary type id to methods with their concrete self so they can be used as function pointers. commit e9315959099d081f0c44aeb6f56784eae1618aa1 Author: Jules Bertholet Date: Wed Mar 27 16:35:40 2024 -0400 Add rustfmt test for mut ref mut commit 7410f78e9a3a8a47bea05bb2c52e0ac307712a68 Author: Nadrieril Date: Tue Mar 5 22:10:48 2024 +0100 Use `create_or_subcandidates` for all or-pattern expansions commit ad51a17c627b4ca57f83f0dc1f3bb5f3f17e6d0b Merge: 4b33850c390 20b12c2bacb Author: bors Date: Wed Mar 27 18:05:45 2024 +0000 Auto merge of #16960 - dfireBird:fix-16958, r=lnicola fix: lifetime length are not added in count of params in highlight I found these two instances easily but I wonder how many such instances are there. Fixes #16958 commit 20b12c2bacb2bba46429806907543a7530a37f8a Author: dfireBird Date: Wed Mar 27 23:00:08 2024 +0530 fix lifetime length are not added in count of params in highlight commit 23c9f698c09d44d6d8ea27fa7631202f26209be3 Author: Nadrieril Date: Mon Mar 4 01:57:39 2024 +0100 Avoid recursion in creating and merging or-patterns By calling back into `match_candidates`, we only need to expand one layer at a time. Conversely, since we always try to simplify a layer that we just expanded, we only have to merge one layer at a time. commit 528d45af18b90ec9833568420a34d4e6050b1ec6 Author: Jules Bertholet Date: Tue Mar 26 01:23:26 2024 -0400 Feature gate commit 7e4bc4a373574ca727254cc7051ee42cb1102e06 Author: Michael Woerister Date: Wed Mar 27 14:57:01 2024 +0100 Remove and disallow HashStable impl of HashMap. commit 11b28d44bd428cdb4c939e51b064426c4b73a293 Author: Jules Bertholet Date: Sat Mar 23 21:04:45 2024 -0400 Implement `mut ref`/`mut ref mut` commit e0da13f25fd223ee1ec20284ea3f4e88a818b804 Author: Jules Bertholet Date: Sat Mar 23 21:04:45 2024 -0400 Implement `mut ref`/`mut ref mut` commit 014230ce1698cd2eed1916129f84e86130ef693b Merge: b8b9b275003 5b95e9099c9 Author: bors Date: Wed Mar 27 13:25:54 2024 +0000 Auto merge of #12572 - y21:mixed_attributes_style_style, r=llogiq Move `mixed_attributes_style` to style > It currently is in suspicious. I wouldn't say that the linted code is "most likely wrong or useless" [...] > :sweat_smile: I would still argue that this doesn't belong in the suspicious group, but rather in the style group. These are some good points made [on zulip](https://rust-lang.zulipchat.com/#narrow/stream/257328-clippy/topic/mixed_attributes_style.20on.20outlined.20modules/near/429823328). ---- changelog: Move [`mixed_attributes_style`] to the `style` category commit 47ed73a7b510b416a191f343c2f07826e8072396 Author: DianQK Date: Sun Mar 10 16:29:39 2024 +0800 Eliminate `UbCheck` for non-standard libraries commit 5b95e9099c96a9f156d7473daf10644471172477 Author: y21 <30553356+y21@users.noreply.github.com> Date: Wed Mar 27 13:06:32 2024 +0100 move `mixed_attributes_style` to the style category commit 2d4b7f287d641b01d666416892498de6b6d7c23c Author: Oli Scherer Date: Wed Mar 27 08:05:24 2024 +0000 Use a `dyn Debug` trait object instead of a closure. Simplifies the API a bit. commit c27f52d611928ed0bb11665e03de21484c2061aa Author: J-ZhengLi Date: Wed Mar 27 16:28:15 2024 +0800 allow [`manual_unwrap_or_default`] in const function commit 8f9a58c73d4181f137f92a83377cd4ca0a9b0259 Author: Alexis (Poliorcetics) Bourget Date: Wed Mar 27 02:29:03 2024 +0100 fix: check for client support of relative glob patterns before using them commit 91f3fad8e732a0c13f58f3c4395fb69d9ec989c6 Author: J-ZhengLi Date: Wed Mar 27 08:36:08 2024 +0800 check for init expr when linting [`question_mark`] commit 4b10cb20bc2eda0cbbc8dfd6a7e33461e2512f15 Merge: 57627d254d2 a6a1f782d63 Author: Matthias Krüger Date: Tue Mar 26 21:23:51 2024 +0100 Rollup merge of #123103 - compiler-errors:inherited-is-a-weird-name, r=oli-obk Rename `Inherited` -> `TypeckRootCtxt` `Inherited` is a confusing name. Rename it to `TypeckRootCtxt`. I don't think this needs a type MCP or anything since it's not nearly as pervasive as `FnCtxt` , for example. r? `@lcnr` `@oli-obk` commit a6a1f782d6347b691376c1ef1e1c3b4727142806 Author: Michael Goulet Date: Tue Mar 26 14:11:51 2024 -0400 Inherited -> TypeckRootCtxt commit 58a771ebfa2e59f48cb50229049487c6550b2f7b Merge: 3c041c4e45e f1519144f28 Author: bors Date: Tue Mar 26 18:03:27 2024 +0000 Auto merge of #3423 - RalfJung:proc-macro-2, r=RalfJung test-cargo-miri: add proc-macro2 This is already in the dependency tree of `serde_derive`, but I guess there is is a host dependency, here it is a target dependency. The logic is presumably the same as in anyhow, so we don't need both; let's test the one that is more widely used. commit f1519144f285da1161ee8e470acc909bb194b3f3 Author: Ralf Jung Date: Tue Mar 26 19:01:51 2024 +0100 update the remaining test-cargo-miri dependencies as well (while we are at it) commit 60937bf2dbf68e36c078087b3a0dc01d6857465a Author: Quinn Sinclair Date: Tue Mar 26 18:59:09 2024 +0100 :adjust applicability for typed identity closures in `filter_map_identity` commit 57627d254d29110fa82c982fedd839d293d0d418 Author: Trevor Gross Date: Tue Mar 26 05:48:13 2024 -0400 Change `f16` and `f128` clippy stubs to be nonpanicking It turns out there is a bit of a circular dependency - I cannot add anything to `core` because Clippy fails, and I can't actually add correct Clippy implementations without new implementations from `core`. Change some of the Clippy stubs from `unimplemented!` to success values and leave a FIXME in their place to mitigate this. Fixes commit 673ff09d6db6ea05dd4a22cabd7b5c9e38d8f964 Author: Ralf Jung Date: Tue Mar 26 18:14:14 2024 +0100 update proc-macro2 commit 4d7d66462f1907d6cd0240c30636e01429fb8f89 Author: Jacob Kiesel Date: Tue Mar 26 10:53:41 2024 -0600 the power of if let chain compels you commit aecdb921aed81bf3dde849e696843deddc84905e Author: Jacob Kiesel Date: Tue Mar 26 10:45:27 2024 -0600 short circuit logic better commit 08c149b73f29f255f3b90178f9fe50838829593f Author: Ralf Jung Date: Tue Mar 26 17:41:08 2024 +0100 rename our proc-macro test crate to a more clear name and remove serde_derive, since the --extern .so file was really just a consequence of building a proc macro commit a379a9557836dde63038f85db371545027cc94e5 Author: Ralf Jung Date: Tue Mar 26 17:28:27 2024 +0100 test-cargo-miri: add proc-macro2 instead of anyhow commit 3c041c4e45e2a3b11848726249015069dd8a2d55 Merge: 9f11ed76ffd a8fb4a0187e Author: bors Date: Tue Mar 26 16:09:03 2024 +0000 Auto merge of #3421 - RalfJung:remove-remove-var, r=RalfJung avoid mutating the global environment `remove_var` is just as bad as `set_var`, let's not do that. commit a8fb4a0187ee741f66f49dbe69dc4f08632d58a7 Author: Ralf Jung Date: Tue Mar 26 17:06:58 2024 +0100 avoid mutating the global environment commit b8b9b275003f0318276ff0285fe7683d466e84c0 Merge: 13ef8457bec e3f3a4b7dc0 Author: bors Date: Tue Mar 26 15:12:23 2024 +0000 Auto merge of #12555 - GuillaumeGomez:duplicated_attribute, r=blyxyas Don't emit `duplicated_attribute` lint on "complex" `cfg`s Part of #12537. changelog: Don't emit `duplicated_attribute` lint on "complex" `cfg`s commit 9f11ed76ffd443badf6f814b9ea06173e148747b Merge: 8b2b84e4831 86b57408c9d Author: bors Date: Tue Mar 26 14:25:03 2024 +0000 Auto merge of #3420 - RalfJung:eyre, r=RalfJung add eyre to test-cargo-miri Same as anyhow: custom build probe, widely used. commit 86b57408c9d82fe4ca651b668147c95ce8c99900 Author: Ralf Jung Date: Tue Mar 26 14:51:43 2024 +0100 add eyre to test-cargo-miri commit e3f3a4b7dc0eb64b92497045db475fa8f0bbe5d0 Author: Guillaume Gomez Date: Mon Mar 25 12:15:48 2024 +0100 Don't emit `duplicated_attribute` lint on "complex" `cfg`s commit 8b2b84e4831426d620ec05ce1f1fcdc321414d3d Merge: 2e198d04ee1 df73cb710e2 Author: bors Date: Tue Mar 26 13:27:35 2024 +0000 Auto merge of #3415 - JoJoDeveloping:tree-borrows-initialized-root, r=RalfJung Tree Borrows: Make tree root always be initialized This PR fixes a slight annoyance we discovered while formally proving that certain optimizations are sound with Tree Borrows. In particular... (copied from the commit message): There should never be an `Active` but not initialized node in the borrow tree. If such a node exists, and if it has a protector, then on a foreign read, it could become disabled. This leads to some problems when formally proving that read moving optimizations are sound. The root node is the only node for which this can happen, since all other nodes can only become `Active` when actually used. But special-casing the root node here is annoying to reason about, everything becomes much nicer if we can simply say that *all* `Active` nodes must be initialized. This requires making the root node default-initialized. This is also more intuitive, since the root arguably becomes initialized during the allocation, which can be considered a write. commit df73cb710e2916a0ed25020f013cae1fb64ccc27 Author: Johannes Hostert Date: Mon Mar 25 17:35:29 2024 +0100 Tree Borrows: Make tree root always be `Active` and initialized There should never be an `Active` but not initialized node in the borrow tree. If such a node exists, and if it has a protector, then on a foreign read, it could become disabled. This leads to some problems when formally proving that read-reordering optimizations are sound. The root node is the only node for which this can happen, since all other nodes can only become `Active` when actually used. But special- casing the root node here is annoying to reason about, everything becomes much nicer if we can simply say that *all* `Active` nodes must be initialized. This requires making the root node default- initialized. This is also more intuitive, since the root arguably becomes ini- tialized during the allocation, which can be considered a write. commit 2e198d04ee1ccd20926701c4da7403d2c6bca7b3 Merge: 78d556e28d4 89a36c03850 Author: bors Date: Tue Mar 26 11:50:27 2024 +0000 Auto merge of #3419 - RalfJung:rustup, r=RalfJung Rustup https://github.com/rust-lang/rust/pull/123081 landed so hopefully this works now. commit 89a36c03850a404d8d3a4b869cb85293e1f9a978 Author: Ralf Jung Date: Tue Mar 26 11:53:50 2024 +0100 fmt commit 78d556e28d4e4d60b45bd183c22748967731abb9 Merge: 5f73da71fb1 5b877a352ea Author: bors Date: Tue Mar 26 10:53:20 2024 +0000 Auto merge of #3418 - RalfJung:cargo-miri, r=RalfJung cargo-miri, miri-script, tests/ui: misc simplifications and comments Parts of https://github.com/rust-lang/miri/pull/3411 that can be landed now. commit c51eb819fe55f458ed9261fc5cc990d29d1baa53 Merge: eee5a77ed03 59c217fed2b Author: Ralf Jung Date: Tue Mar 26 11:52:02 2024 +0100 Merge from rustc commit eee5a77ed0352817e32a37832f144c3364394ff8 Author: Ralf Jung Date: Tue Mar 26 11:51:57 2024 +0100 Preparing for merge from rustc commit 5b877a352ea2542eb744302815e686e7007c09e0 Author: Ralf Jung Date: Mon Mar 25 11:07:01 2024 +0100 cargo-miri: clean up info_query treatment a bit, and update comment about RUSTC commit a9b4a9413f1e8d1b291556b136e48f7419afa202 Author: Ralf Jung Date: Tue Mar 26 11:42:38 2024 +0100 run command: simplify flag computation commit 22b00b997655c78855f4d5b1576f84bb7ee8efe2 Author: Ralf Jung Date: Mon Mar 25 11:02:20 2024 +0100 run_dep_mode: treat program.args and program.env consistently commit 13ef8457bec4a575b765b52d56a57fabd6213212 Merge: 805ef35ca14 b34afba5fbf Author: bors Date: Tue Mar 26 09:01:41 2024 +0000 Auto merge of #12481 - xFrednet:add-team-docs, r=flip1995 RFC: Document Clippy's teams and team duties First the big announcement: **We want to add a new subteam for regular contributors to give them triage rights.** --- This PR adds a new section to the book which describes the Clippy and Clippy-Contributor teams, with their duties and membership requirements. This is just an initial draft, that outlines what, I think, their responsibilities should be. I hope everyone in the team is okay with me posting this directly to GitHub. I think a PR makes collaboration a bit easier. [:framed_picture: Rendered :framed_picture:](https://github.com/xFrednet/rust-clippy/blob/add-team-docs/book/src/development/the_team.md) --- Once we've decided on this document, I'll create a PR to add the new team on GitHub. As part of this, we'll also reach out to some active contributors, to ask if they would like to join the new team. --- cc: `@rust-lang/clippy` cc: #6627 changelog: none r? `@flip1995` commit 4b33850c39049047e2deb4269b60bd8330b68031 Merge: 0583aaa5553 85947bba495 Author: bors Date: Tue Mar 26 09:00:24 2024 +0000 Auto merge of #16951 - lnicola:provenance-split-comment, r=lnicola minor: Update comment on provenance_split Hope I got this right `@dfireBird.` commit 85947bba495e5029500046b01a745658ea8fe108 Author: Laurențiu Nicola Date: Tue Mar 26 10:58:35 2024 +0200 Update comment on provenance_split commit 0583aaa5553365edb7099a6382bba55ee822c455 Merge: e52bb8cddb0 0e54e2b55ac Author: bors Date: Tue Mar 26 07:58:43 2024 +0000 Auto merge of #16805 - dfireBird:lifetime_lowering, r=Veykril feat: Implement resolving and lowering of Lifetimes (no inference yet) commit 5f73da71fb16d5e4410dac3190ba95a0fb9afb2a Merge: cf1eb93a023 377caa2afd8 Author: bors Date: Tue Mar 26 07:47:32 2024 +0000 Auto merge of #3417 - RalfJung:RUSTC_WORKSPACE_WRAPPER, r=RalfJung we have to ignore RUSTC_WORKSPACE_WRAPPER as well This matches what we do with RUSTC_WRAPPER. commit 0e54e2b55ac3d2ddec269978c86787e7320aaa7a Author: dfireBird Date: Tue Mar 19 12:57:18 2024 +0530 use references in Generics iter methods commit 377caa2afd81c3425ba9e82409ea25d5e9d593a8 Author: Ralf Jung Date: Tue Mar 26 08:27:13 2024 +0100 we have to ignore RUSTC_WORKSPACE_WRAPPER as well commit 805ef35ca14b265f5ee9b5ea3f06a12736ec14f9 Merge: ca6a6474904 94fe2fac633 Author: bors Date: Mon Mar 25 23:09:33 2024 +0000 Auto merge of #12540 - m-rph:12366, r=blyxyas Remove `unwrap` from `match_trait_method` Unused_IO_amount relies on `match_trait_method` in order to match trait methods that exist in Tokio traits as the corresponding symbols don't exist. With this commit we remove the unwrap that caused #12366. Note: author (`@m-rph)` and `@GuillaumeGomez` couldn't replicate #12366. changelog:none r? `@blyxyas` commit 9655231eb825500d69738382f791374660311390 Author: Quinn Sinclair Date: Mon Mar 25 23:29:34 2024 +0100 Allow `filter_map_identity` when the closure is typed This extends the `filter_map_identity` lint to support typed closures. For untyped closures, we know that the program compiles, and therefore we can safely suggest using flatten. For typed closures, they may participate in type resolution. In this case we use `Applicability::MaybeIncorrect`. Details: https://rust-lang.zulipchat.com/#narrow/stream/257328-clippy/topic/Should.20.60filter_map_identity.60.20lint.20when.20closures.20are.20typed.3F commit e52bb8cddb0d636a86a3560e9eadb5f3d8f8c2af Merge: 9b79fa23939 58013ad70d2 Author: bors Date: Mon Mar 25 22:13:59 2024 +0000 Auto merge of #16906 - Young-Flash:limit_struct_hover_display, r=Veykril feat: limit struct hover display nums follow up https://github.com/rust-lang/rust-analyzer/pull/15847, https://github.com/rust-lang/rust-analyzer/pull/15938 commit 9b79fa2393949c13a7018ea49752985c8b03d647 Merge: dacfa72f85c f6b9cff105f Author: bors Date: Mon Mar 25 22:01:23 2024 +0000 Auto merge of #16930 - lnicola:dist-malloc, r=Veykril internal: Support choosing the allocator in `xtask dist` commit ca6a6474904fc7d3e4519a3b5506294b33a12ea8 Merge: b1b73528804 dafb7f6d63a Author: bors Date: Mon Mar 25 21:39:32 2024 +0000 Auto merge of #12554 - Kobzol:assigning-clones-unspecified, r=blyxyas Change applicability of `assigning_clones` to `Unspecified` Before we deal with https://github.com/rust-lang/rust-clippy/pull/12473 and the borrow checker errors, I think that it would be better to downgrade this lint, since it can break code. changelog: Change the applicability of `assigning_clones` to `Unspecified` r? `@blyxyas` commit b1b735288049692c9495f1f89fc948071b4a24b4 Merge: 4ef57d3a709 c137c78ba27 Author: bors Date: Mon Mar 25 20:07:54 2024 +0000 Auto merge of #12536 - samueltardieu:issue-12505, r=Manishearth `manual_assert`: do not add extra semicolon Fixes #12505 changelog: [`manual_assert`]: do not add extra semicolon to suggestion commit 4ef57d3a709d033fa0791ba20d2766a145f8cc03 Merge: c3948d16b92 9e82ad87b9a Author: bors Date: Mon Mar 25 19:56:18 2024 +0000 Auto merge of #12558 - y21:issue9150, r=xFrednet [`let_and_return`]: avoid linting when code between last stmt and return expr is cfg'd out Fixes #9150 This moves `span_contains_cfg` to utils and starts using it in `let_and_return` as well. changelog: [`let_and_return`]: avoid linting when code between the last statement and the final return expression is `#[cfg]`ed out commit 9e82ad87b9ae43bee6f06308631fbe0fb0a1c847 Author: y21 <30553356+y21@users.noreply.github.com> Date: Mon Mar 25 17:47:20 2024 +0000 [`let_and_return`]: avoid linting when `#[cfg]` attributes are present commit c3948d16b928616407f1eac19bfea9ba9e2df16e Merge: be27f68d260 b9da637655c Author: bors Date: Mon Mar 25 16:51:43 2024 +0000 Auto merge of #12549 - granddaifuku:fix/suspicious_else_formatting-false-positive-when-commented-else, r=Alexendoo fix: `suspicious_else_formatting` false positive when else is included … This PR addresses an issue where invalid suggestions are generated for `if-else` formatting if comments contain the keyword `else`. The root of the problem is identified [here](https://github.com/rust-lang/rust-clippy/blob/95c62ffae9bbce793f68a6f1473e3fc24af19bdd/clippy_lints/src/formatting.rs#L217). Specifically, when a comment contains the word `else`, the lint mistakenly interprets it as part of an `if-else` clause. This misinterpretation leads to an incorrect splitting of the snippet, resulting in erroneous suggestions. fixes: #12497 changelog: [`suspicious_else_formatting`]: Fixes invalid suggestions when comments include word else commit be27f68d26042408b705dd00bce414966000cadc Merge: 95c62ffae9b e0b6f30397b Author: bors Date: Mon Mar 25 16:19:00 2024 +0000 Auto merge of #12557 - stanislav-tkach:unconditional-recursion-remove-dot, r=y21 Remove unnecessary dot in the 'unconditional recursion' lint description I don't think such changes should be reflected in the changelog. changelog: none commit e0b6f30397ba152a7bd190d98c1389383e276ba3 Author: Stanislav Tkach Date: Mon Mar 25 17:10:26 2024 +0100 Remove unnecessary dot in the 'unconditional recursion' lint description commit b9da637655c4e1c66dde2fb291ae4786b2b7e057 Author: granddaifuku Date: Tue Mar 26 00:46:57 2024 +0900 Refine the logic to accurately assess if 'else' resides within comments commit dacfa72f85c261e1596ae2a3579be338e288f22a Merge: 6f6b03f9de7 1716cc8433f Author: bors Date: Mon Mar 25 12:17:18 2024 +0000 Auto merge of #16944 - Nadrieril:update-pat-ana, r=lnicola Revert to the crates.io version of rustc_pattern_analysis The API hasn't fully settled yet, and there's an extra wrinkle with `IdxContainer` which blocked the [subtree update](https://github.com/rust-lang/rust/pull/122981). Let's just keep using the crates.io version for a bit longer. r? `@lnicola` commit 1716cc8433fe4822cf531905b8d132c23592c72c Author: Nadrieril Date: Mon Mar 25 13:09:37 2024 +0100 Revert to the crates.io version of rustc_pattern_analysis commit 58013ad70d2c8a204d311fdacb40a80ba87f37c4 Author: Young-Flash Date: Mon Mar 25 19:55:09 2024 +0800 add test for struct field hover display limit commit ba8c9810aadc2e2773ba1c89b621d100a39e35df Author: Young-Flash Date: Sat Mar 23 08:42:45 2024 +0800 review update commit a89e417ce5b23b304ec17d05ca2786ba364f1d67 Author: Young-Flash Date: Thu Mar 21 17:58:44 2024 +0800 adjust test commit 1c85234bcdbc7c863e737e9f3329518beeb436c3 Author: Young-Flash Date: Thu Mar 21 17:58:29 2024 +0800 limit struct field hover display nums commit d81148a0095e97f00720d6bbeee098130b304696 Author: Young-Flash Date: Thu Mar 21 17:56:49 2024 +0800 expose config for hover struct field display commit dafb7f6d63acf750e4618e5ebd8df20d7cf2f0b1 Author: Jakub Beránek Date: Mon Mar 25 10:24:13 2024 +0100 Change applicability of `assigning_clones` to `Unspecified` commit 71eb763d23c0406b14237be60c651d52c64d29b6 Author: Martin Nordholts Date: Sat Feb 24 16:48:24 2024 +0100 unix_sigpipe: Add test for SIGPIPE disposition in child processes For robustness, also test the disposition in our own process even if other tests in `tests/ui/attributes/unix_sigpipe` already covers it. commit a2a2f30415ffdcfad86cd5f9948e7cfc415164c4 Author: Martin Nordholts Date: Mon Mar 25 06:21:42 2024 +0100 test-aux-bin.rs: Clarify that it is aux-bin that blocks cross-compile run-pass commit c59e93c753ba7e5945fe95f4d1785b09300aa0e0 Author: Scott McMurray Date: Sun Mar 24 17:13:26 2024 -0700 Address PR feedback commit 6f6b03f9de783f91456080b3f6adc8d92903c1b0 Merge: 3dfd4c1f4c9 2dfe7de8b66 Author: bors Date: Sun Mar 24 18:17:36 2024 +0000 Auto merge of #16935 - Nilstrieb:dont-panic, r=HKalbasi Handle panicking like rustc CTFE does Instead of using `core::fmt::format` to format panic messages, which may in turn panic too and cause recursive panics and other messy things, redirect `panic_fmt` to `const_panic_fmt` like CTFE, which in turn goes to `panic_display` and does the things normally. See the tests for the full call stack. The tests don't work yet, I probably missed something in minicore. fixes #16907 in my local testing, I also need to add a test for it commit 2a62200b8d5ba65d583199a296c3fbbf8e58aa25 Author: granddaifuku Date: Mon Mar 25 02:17:56 2024 +0900 fix: suspicious_else_formatting false positive when else is included in comments commit 8d5977d6af03af18c7851025b9ca70d0f2d12c86 Author: Scott McMurray Date: Sun Mar 24 10:01:37 2024 -0700 Slightly simplify the `iN::partial_cmp` MIR This saves some debug and scope metadata in every single function that calls it. Normally wouldn't be worth it, but with the derives there's *so* many of these. commit f2e91ab1b9aaab23b81fc5ea5de43367cb9b9e6d Author: Peter Jaszkowiak Date: Sun Mar 24 10:16:45 2024 -0600 match syms, remove lint_reasons commit 3dfd4c1f4c955e13d5936a3ffa2a19661c97cd07 Merge: e265e3d5189 142ef764ee3 Author: bors Date: Sun Mar 24 15:05:14 2024 +0000 Auto merge of #16915 - 6d7a:master, r=HKalbasi fix: Prevent stack overflow in recursive const types In the evaluation of const values of recursive types certain declarations could cause an endless call-loop within the interpreter (hir-ty’s create_memory_map), which would lead to a stack overflow. This commit adds a check that prevents values that contain an address in their value (such as TyKind::Ref) from being allocated at the address they contain. The commit also adds a test for this edge case. commit 733c7af87f44a2e71540d3ff12c66f1a0a1a6b16 Author: Alex Macleod Date: Sun Mar 24 14:57:57 2024 +0000 Rename `{enter,exit}_lint_attrs` to `check_attributes{,_post}` commit 2dfe7de8b66c17b3f9025766597b8399394527b8 Author: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sun Mar 24 15:39:15 2024 +0100 Handle panicking like rustc CTFE does Instead of using `core::fmt::format` to format panic messages, which may in turn panic too and cause recursive panics and other messy things, redirect `panic_fmt` to `const_panic_fmt` like CTFE, which in turn goes to `panic_display` and does the things normally. See the tests for the full call stack. commit 94fe2fac633e4a17f9ba63ee74689beae62d24b6 Author: Quinn Sinclair Date: Sun Mar 24 11:00:09 2024 +0100 Remove `unwrap` from `match_trait_method` Unused_IO_amount relies on `match_trait_method` in order to match trait methods that exist in Tokio traits as the corresponding symbols don't exist. With this commit we remove the unwrap that may have caused 12366. Note: author (@m-rph) and @GuillaumeGomez couldn't replicate 12366. commit 142ef764ee3af3cee8d6336ee153a9e3f13fec90 Author: 6d7a Date: Sun Mar 24 10:19:25 2024 +0100 fix: Check stack depth to prevent stack overflows in create_memory_map commit 629a772585f9d003e583a8b60658692ac5a47c9e Author: Scott McMurray Date: Sun Mar 5 20:19:41 2023 -0800 Add+Use `mir::BinOp::Cmp` commit 3da115a93b782796e3d267266b4241e5258f1fef Author: Scott McMurray Date: Sun Mar 5 20:19:41 2023 -0800 Add+Use `mir::BinOp::Cmp` commit 744c664ba2e6440024457d5ec0d3600b3e0c0144 Author: Scott McMurray Date: Sun Nov 26 00:36:56 2023 -0800 Add a MIR pre-codegen test for derived PartialOrd commit 0cf9d9c440044b07351f0d3405fc675de8885c09 Author: Jacob Kiesel Date: Sat Mar 23 20:59:30 2024 -0600 restrict manual_clamp to const case, bring it out of nursery commit 95c62ffae9bbce793f68a6f1473e3fc24af19bdd Merge: 12f7c17ae0e ee2558223f5 Author: bors Date: Sun Mar 24 00:20:28 2024 +0000 Auto merge of #12239 - GuillaumeGomez:missing_transmute_annotation, r=y21 Add `missing_transmute_annotations` lint Fixes https://github.com/rust-lang/rust-clippy/issues/715. r? `@blyxyas` changelog: Add `missing_transmute_annotations` lint commit 643029693b2987b4b7c8b5073e8a25536cca7069 Author: Andy Kurnia Date: Sun Mar 24 08:15:00 2024 +0800 clarify equivalency of binary_search and partition_point commit ee2558223f51b894475dcd1458b21f7ea393508a Author: Guillaume Gomez Date: Fri Mar 15 18:42:35 2024 +0100 Do no emit `missing_transmute_annotations` lint if the `transmute` is the only expr in the function commit ffa12798c0f24fb2fe8659aa917a9b9308f8bc7b Author: Guillaume Gomez Date: Sun Feb 25 18:12:32 2024 +0100 Correctly handle `transmute` as return value from `block` and `let var: _ = transmute` commit 8e0496170d7f655f1e14887e22e12f7795bdb394 Author: Guillaume Gomez Date: Mon Feb 19 14:53:53 2024 +0100 Add ui test for `missing_transmute_annotations` commit 905550149f5c85cd17f4eadf55d0fdc8e6420777 Author: Ralf Jung Date: Sat Mar 23 23:00:53 2024 +0100 also rename the SIMD intrinsic commit f2cff5ebb9ebc9712eec88dd5ec578f76efb33bc Author: Ralf Jung Date: Sat Mar 23 23:00:53 2024 +0100 also rename the SIMD intrinsic commit 47265adb1ac174a457f2c1e186ea66f391b3b7f9 Merge: 21a6f0ce87f 5919b26d33a Author: bors Date: Sat Mar 23 21:11:00 2024 +0000 Auto merge of #122629 - RalfJung:assert-unsafe-precondition, r=saethlin refactor check_{lang,library}_ub: use a single intrinsic This enacts the plan I laid out [here](https://github.com/rust-lang/rust/pull/122282#issuecomment-1996917998): use a single intrinsic, called `ub_checks` (in aniticpation of https://github.com/rust-lang/compiler-team/issues/725), that just exposes the value of `debug_assertions` (consistently implemented in both codegen and the interpreter). Put the language vs library UB logic into the library. This makes it easier to do something like https://github.com/rust-lang/rust/pull/122282 in the future: that just slightly alters the semantics of `ub_checks` (making it more approximating when crates built with different flags are mixed), but it no longer affects whether these checks can happen in Miri or compile-time. The first commit just moves things around; I don't think these macros and functions belong into `intrinsics.rs` as they are not intrinsics. r? `@saethlin` commit aa9c9a36c06ac33275be60b840d1b954f8b1eac3 Author: John Kåre Alsaker Date: Sat Mar 23 20:23:25 2024 +0100 Add some comments and do some renames commit 6119763e199b1cf92a1a43d3511028f67e68986f Author: John Kåre Alsaker Date: Sun Sep 24 01:34:45 2023 +0200 Encode dep graph edges directly from the previous graph when promoting commit 5919b26d33aac5da7e78468c4412814869b0b12f Author: Ralf Jung Date: Sun Mar 17 10:12:25 2024 +0100 move assert_unsafe_preconditions to its own file These macros and functions are not intrinsics, after all. commit 21a6f0ce87f4b3c46c43ed3fc9d387e72192e7ca Merge: e5ece90f640 43a61e9aca8 Author: Matthias Krüger Date: Sat Mar 23 15:00:18 2024 +0100 Rollup merge of #122780 - GuillaumeGomez:rename-hir-local, r=oli-obk Rename `hir::Local` into `hir::LetStmt` Follow-up of #122776. As discussed on [zulip](https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Improve.20naming.20of.20.60ExprKind.3A.3ALet.60.3F). I made this change into a separate PR because I'm less sure about this change as is. For example, we have `visit_local` and `LocalSource` items. Is it fine to keep these two as is (I supposed it is but I prefer to ask) or not? Having `Node::Local(LetStmt)` makes things more explicit but is it going too far? r? ```@oli-obk``` commit 5afe4a9e09b09b2a74cf5cbce5b69ca40b3873a9 Author: Andy Kurnia Date: Sat Mar 23 21:38:32 2024 +0800 improve example on inserting to a sorted vector to avoid shifting equal elements commit 4d623015e0c993aea542834b44fc4dd18b62483e Author: Ralf Jung Date: Sat Mar 23 12:21:20 2024 +0100 rename MIR int2ptr casts to match library name commit c7ab6b5030a320cbc34b730fdc8d06bac6f7f438 Author: Ralf Jung Date: Sat Mar 23 12:21:20 2024 +0100 rename MIR int2ptr casts to match library name commit 038e7c6c38b9e1713fb258c783441bb0102b8492 Author: Ralf Jung Date: Sat Mar 23 12:21:20 2024 +0100 rename MIR int2ptr casts to match library name commit 67b9d7d18468d1f0bddbca3cdc6878f6ef3b5a12 Author: Ralf Jung Date: Sat Mar 23 11:47:11 2024 +0100 rename ptr::from_exposed_addr -> ptr::with_exposed_provenance commit 12f7c17ae0e9137a4b470d17c5ed204458f4bab2 Merge: db416211d6f fed2f28223c Author: bors Date: Sat Mar 23 10:46:18 2024 +0000 Auto merge of #12535 - samueltardieu:issue-12528, r=y21 `useless_asref`: do not lint `.as_ref().map(Arc::clone)` This applies to `Arc`, `Rc`, and their weak variants. Using `.clone()` would be less idiomatic. This follows the discussion in . changelog: [`useless_asref`]: do not lint `.as_ref().map(Arc::clone)` and similar commit f6b9cff105f8c731f3b6bb81086a7405b71ca74c Author: Laurențiu Nicola Date: Sat Mar 23 11:48:21 2024 +0200 Support choosing the allocator in xtask dist commit 84d38c7e94345ff2338c79584fd3b98087e7b577 Author: Laurențiu Nicola Date: Sat Mar 23 11:35:32 2024 +0200 Move xtask flags around outide the generated section commit fed2f28223c43e56da6a31e6eb8c3fd5d84606d2 Author: Samuel Tardieu Date: Sat Mar 23 00:24:30 2024 +0100 Do not rewrite `.as_ref().map(Arc::clone)` and similar commit 02fc25635e88b670f0b4bfe5afdcf4ab2771d3e0 Author: Samuel Tardieu Date: Sat Mar 23 00:23:59 2024 +0100 Add `should_call_clone_as_function()` utility function commit e265e3d5189513c42d037999d41a696369af9388 Merge: 903158268aa 869efe23c0e Author: bors Date: Sat Mar 23 08:09:18 2024 +0000 Auto merge of #16929 - lnicola:bump-release-actions, r=lnicola internal: Bump release actions and Node commit 869efe23c0e414509f2c49b67543e0a6d231648a Author: Laurențiu Nicola Date: Sat Mar 23 09:54:50 2024 +0200 Bump actions/checkout, actions/setup-node and node in release workflow commit 903158268aabf6141086e4ae427e91d56144ca97 Merge: 6e54d41d5a7 6ee3f663861 Author: bors Date: Sat Mar 23 07:37:54 2024 +0000 Auto merge of #16928 - lnicola:rocky-8, r=lnicola internal: Build x86_64-unknown-linux-gnu releases on Rocky Linux 8 commit 6ee3f663861bd2f4818d0965ad3cd4e8534e5578 Author: Laurențiu Nicola Date: Sat Mar 23 08:48:31 2024 +0200 Build x86_64-unknown-linux-gnu on Rocky Linux 8 commit 6e54d41d5a710e286e081860c86e8e93bf81b0b2 Merge: 6f54ebb30c5 966d387afac Author: bors Date: Sat Mar 23 05:39:28 2024 +0000 Auto merge of #16927 - Sculas:fix/funclike-rename, r=Veykril fix: Rename `func_like` to `FuncLike` Should fix #16926. Please check the issue for more information. commit db416211d6fd2847102ad4ade06582610c416cb0 Merge: 4a8c9495ca7 e9f25b3b096 Author: bors Date: Sat Mar 23 04:28:19 2024 +0000 Auto merge of #12486 - J-ZhengLi:issue12435, r=y21 don't lint [`mixed_attributes_style`] when mixing docs and other attrs fixes: #12435 fixes: #12436 fixes: #12530 --- changelog: don't lint [`mixed_attributes_style`] when mixing different kind of attrs; and move it to late pass; commit e9f25b3b096a8dba595fee21ba42e255563a55b5 Author: J-ZhengLi Date: Thu Mar 14 17:06:18 2024 +0800 add test cases for #12435 don't lint [`mixed_attributes_style`] when mixing docs and other attrs add test files for issue #12436 move [`mixed_attributes_style`] to `LateLintPass` to enable global `allow` stop [`mixed_attributes_style`] from linting on different attributes add `@compile-flags` to [`mixed_attributes_style`]'s test; turns out not linting in test mod is not a FN. Apply suggestions from code review Co-authored-by: Timo <30553356+y21@users.noreply.github.com> move [`mixed_attributes_style`] to late pass and stop it from linting on different kind of attributes commit e5ece90f64037e72b861a6e7866f6b6473f2cb44 Merge: 02e11b98ddf cbbb0ae7d2c Author: bors Date: Sat Mar 23 00:37:05 2024 +0000 Auto merge of #119552 - krtab:dead_code_priv_mod_pub_field, r=cjgillot,saethlin Replace visibility test with reachability test in dead code detection Fixes https://github.com/rust-lang/rust/issues/119545 Also included is a fix for an error now flagged by the lint commit c137c78ba2718134ad7937ee4ad5034c296b70df Author: Samuel Tardieu Date: Sat Mar 23 01:32:25 2024 +0100 `manual_assert`: do not add extra semicolon commit 6b12829943aa617b17965b1c25d898d09406eab6 Author: Samuel Tardieu Date: Sat Mar 23 01:27:14 2024 +0100 Move `is_parent_stmt` to `clippy_utils` commit c65f7d8ff181217e96662ec0ca4e5e6d9507d3dc Author: Michael Howell Date: Fri Mar 22 17:00:38 2024 -0700 rustdoc-search: address nits commit 02e11b98ddf92a5c5850230e66a5aca7761d2217 Merge: f9ad628acd8 8b14a369962 Author: bors Date: Fri Mar 22 22:35:11 2024 +0000 Auto merge of #122900 - matthiaskrgr:rollup-nls90mb, r=matthiaskrgr Rollup of 8 pull requests Successful merges: - #114009 (compiler: allow transmute of ZST arrays with generics) - #122195 (Note that the caller chooses a type for type param) - #122651 (Suggest `_` for missing generic arguments in turbofish) - #122784 (Add `tag_for_variant` query) - #122839 (Split out `PredicatePolarity` from `ImplPolarity`) - #122873 (Merge my contributor emails into one using mailmap) - #122885 (Adjust better spastorino membership to triagebot's adhoc_groups) - #122888 (add a couple more tests) r? `@ghost` `@rustbot` modify labels: rollup commit 4a8c9495ca72ffc4e25ec75984d5678c5b9ae796 Merge: c7bb2000546 2d499d8f4aa Author: bors Date: Fri Mar 22 22:27:49 2024 +0000 Auto merge of #12534 - JMoogs:patch-1, r=y21 Fix typo in exhaustive_items.rs changelog: none commit 2d499d8f4aa5c7a4739898057dafc4dbf67ea3c7 Author: Jeremy S Date: Fri Mar 22 22:19:31 2024 +0000 Fix typo in exhaustive_items.rs commit 966d387afac9c7d0be9937fae07a030166898d25 Author: Sculas Date: Fri Mar 22 23:03:44 2024 +0100 fix: Rename `func_like` to `FuncLike` Fixes rust-lang/rust-analyzer#16926 commit 5333f2a9d156a8c1934da11c505dc91a29f5db08 Author: Michael Goulet Date: Fri Mar 22 14:45:00 2024 -0400 Move check for error in impl header outside of reporting commit c7bb20005464b69eeb0b56202b2ede49802c2c60 Merge: 44a5edaaabe 2ffd1336c72 Author: bors Date: Fri Mar 22 19:53:26 2024 +0000 Auto merge of #12532 - samueltardieu:issue-12531, r=llogiq Add necessary parentheses to `manual_unwrap_or_default` lint output Fix #12531 ---- changelog: [`manual_unwrap_or_default`]: add parentheses to suggestion when appropriate commit 43a61e9aca89e9d872e00222e92318d5043ca87c Author: Guillaume Gomez Date: Fri Mar 22 18:06:20 2024 +0100 Rename `hir::Node::Local` into `hir::Node::LetStmt` commit bd9efd5265115956d9a0eec09c34f95898643603 Author: Guillaume Gomez Date: Wed Mar 20 17:50:31 2024 +0100 Rename `hir::Local` into `hir::LetStmt` commit 8b14a369962b2197c12e12a2f817487aa0868356 Merge: 1c37a237002 6b04fc24fc4 Author: Matthias Krüger Date: Fri Mar 22 20:31:30 2024 +0100 Rollup merge of #122839 - compiler-errors:predicate-polarity, r=lcnr Split out `PredicatePolarity` from `ImplPolarity` Because having to deal with a third `Reservation` level in all the trait solver code is kind of weird. r? `@lcnr` or `@oli-obk` commit 1c37a237002abe01727de97b096aad2f947414ce Merge: 879899ca896 f9a9c4bf3bc Author: Matthias Krüger Date: Fri Mar 22 20:31:28 2024 +0100 Rollup merge of #122195 - jieyouxu:impl-return-note, r=fmease Note that the caller chooses a type for type param ``` error[E0308]: mismatched types --> $DIR/return-impl-trait.rs:23:5 | LL | fn other_bounds() -> T | - - | | | | | expected `T` because of return type | | help: consider using an impl return type: `impl Trait` | expected this type parameter ... LL | () | ^^ expected type parameter `T`, found `()` | = note: expected type parameter `T` found unit type `()` = note: the caller chooses the type of T which can be different from () ``` Tried to see if "expected this type parameter" can be replaced, but that goes all the way to `rustc_infer` so seems not worth the effort and can affect other diagnostics. Revives #112088 and #104755. commit 44a5edaaabe9e9191fea86c9443911ba2cf3cf1c Merge: 52b2a5e50d3 a24d12b7aa0 Author: bors Date: Fri Mar 22 16:15:06 2024 +0000 Auto merge of #12507 - Alexendoo:unused-qualifications, r=dswij Enable unused_qualifications lint Fixes a common nit changelog: none commit a24d12b7aa0d5e4d94b5d4f8467d21986d906de3 Author: Alex Macleod Date: Mon Mar 18 21:28:43 2024 +0000 Enable unused_qualifications lint commit 6b04fc24fc4ade1a03b9c47596fb3ec14a3349c2 Author: Michael Goulet Date: Thu Mar 21 15:49:17 2024 -0400 Fix clippy commit f9ad628acd819bb38de972edc777246311f021f7 Author: Michael Goulet Date: Thu Mar 21 17:42:46 2024 -0400 And the tools too commit c92b350581583c251dcd447f92fc20d362d34834 Author: Michael Goulet Date: Thu Mar 21 17:11:06 2024 -0400 Programmatically convert some of the pat ctors commit 0b810866ef8ffe4125f9c7aebb7d8b3792d2b7f1 Author: Michael Goulet Date: Thu Mar 21 16:50:21 2024 -0400 Eagerly convert some ctors to use their specialized ctors commit 52b2a5e50d37149403a692153127cc4aac686a7f Merge: b5e73944007 7c9fe30ce43 Author: bors Date: Fri Mar 22 15:11:52 2024 +0000 Auto merge of #12529 - samueltardieu:issue-12528, r=y21 Do not warn on .map(_::clone) for Arc, Rc, and their weak variants Those constructions are idiomatic, and using `Arc::clone(x)` and `Rc::clone(x)` is often the recommended way of cloning a `Arc` or a `Rc`. Fix #12528 changelog: [`map_clone`]: do not warn on `.map(_::clone)` for `Arc`, `Rc`, and their `Weak` variants commit 2ffd1336c720190fe26cf27324c111c41bfa9a8a Author: Samuel Tardieu Date: Fri Mar 22 12:21:35 2024 +0100 Add necessary parentheses to `manual_unwrap_or_default` lint output commit b5e739440075ae0d38b2ee74ed34af627a54f92c Merge: f2020c884f1 b392e478254 Author: bors Date: Fri Mar 22 14:17:09 2024 +0000 Auto merge of #12533 - Alexendoo:remove-tester, r=flip1995 Remove unused dep `tester` changelog: none commit 05783c8ed68f7b393d28f5df64f98d3d1eee5537 Author: Mark Rousskov Date: Sun Mar 17 22:26:39 2024 -0400 Codegen const panic messages as function calls This skips emitting extra arguments at every callsite (of which there can be many). For a librustc_driver build with overflow checks enabled, this cuts 0.7MB from the resulting binary. commit 00f4daa27673a07bf9ad20f4707d97bc1079450f Author: Mark Rousskov Date: Sun Mar 17 22:26:39 2024 -0400 Codegen const panic messages as function calls This skips emitting extra arguments at every callsite (of which there can be many). For a librustc_driver build with overflow checks enabled, this cuts 0.7MB from the resulting binary. commit b392e4782541195a0d3c6b367297fb98e4eddc50 Author: Alex Macleod Date: Fri Mar 22 13:44:28 2024 +0000 Remove unused dep `tester` commit f2020c884f1a47fec9a2fa26c5ca0ec58b5e1271 Merge: 403433f2f7a 3930f8b45d4 Author: bors Date: Fri Mar 22 13:19:09 2024 +0000 Auto merge of #12508 - y21:issue12506, r=llogiq Fix infinite loop in `cast_sign_loss` when peeling unwrap method calls Fixes #12506 The lint wants to peel method calls but didn't actually reassign the expression, leading to an infinite loop. ---- changelog: Fix infinite loop in [`cast_sign_loss`] when having two chained `.unwrap()` calls commit 403433f2f7abecb5afb586b1a18fdd44460d1bec Merge: fc053c32967 15da6e735a4 Author: bors Date: Fri Mar 22 13:09:28 2024 +0000 Auto merge of #12526 - kpreid:patch-2, r=blyxyas Mention `size_hint()` effect in `flat_map_option` lint documentation. The previous documentation for `flat_map_option` mentioned only readability benefits, but there is also at least one performance benefit: the `size_hint()` upper bound is preserved, whereas `flat_map().size_hint()` is always `(0, None)`. Program demonstrating this difference: ```rust fn main() { let evens = |i| if i % 2 == 0 { Some(i) } else { None }; dbg!( [1, 2, 3].iter().flat_map(evens).size_hint(), [1, 2, 3].iter().filter_map(evens).size_hint(), ); } ``` changelog: [`flat_map_option`]: Mention the benefit to `size_hint()`. commit 879899ca8963e8035172b649169143824a849f5d Merge: b7026f87f52 a580b4e6e03 Author: bors Date: Fri Mar 22 12:29:42 2024 +0000 Auto merge of #122869 - matthiaskrgr:rollup-0navj4l, r=matthiaskrgr Rollup of 9 pull requests Successful merges: - #121619 (Experimental feature postfix match) - #122370 (Gracefully handle `AnonConst` in `diagnostic_hir_wf_check()`) - #122537 (interpret/allocation: fix aliasing issue in interpreter and refactor getters a bit) - #122542 (coverage: Clean up marker statements that aren't needed later) - #122800 (Add `NonNull::<[T]>::is_empty`.) - #122820 (Stop using `` in various diagnostic situations) - #122847 (Suggest `RUST_MIN_STACK` workaround on overflow) - #122855 (Fix Itanium mangling usizes) - #122863 (add more ice tests ) r? `@ghost` `@rustbot` modify labels: rollup commit a580b4e6e03a462b79fc2c468db76f5be1a051ca Merge: 0e62b184353 c36d5e3280f Author: Matthias Krüger Date: Fri Mar 22 11:36:58 2024 +0100 Rollup merge of #121619 - RossSmyth:pfix_match, r=petrochenkov Experimental feature postfix match This has a basic experimental implementation for the RFC postfix match (rust-lang/rfcs#3295, #121618). [Liaison is](https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Postfix.20Match.20Liaison/near/423301844) ```@scottmcm``` with the lang team's [experimental feature gate process](https://github.com/rust-lang/lang-team/blob/master/src/how_to/experiment.md). This feature has had an RFC for a while, and there has been discussion on it for a while. It would probably be valuable to see it out in the field rather than continue discussing it. This feature also allows to see how popular postfix expressions like this are for the postfix macros RFC, as those will take more time to implement. It is entirely implemented in the parser, so it should be relatively easy to remove if needed. This PR is split in to 5 commits to ease review. 1. The implementation of the feature & gating. 2. Add a MatchKind field, fix uses, fix pretty. 3. Basic rustfmt impl, as rustfmt crashes upon seeing this syntax without a fix. 4. Add new MatchSource to HIR for Clippy & other HIR consumers commit 6f54ebb30c56f6f5bbda8bb312632c8e9c0c32b9 Merge: fc0d51ae655 8e324e98a1d Author: bors Date: Fri Mar 22 10:22:43 2024 +0000 Auto merge of #16905 - Veykril:sysroot-patch-cfg, r=Veykril internal: Rename ProcMacroKind::FuncLike to Bang commit 8e324e98a1db06b3be105c8e3e905665653233a1 Author: Lukas Wirth Date: Thu Mar 21 09:33:17 2024 +0100 Rename ProcMacroKind::FuncLike to Bang commit fc053c3296749bbb6fe29af83061c2bfc99d582a Merge: 9d6f41691ed 87b93520a82 Author: bors Date: Fri Mar 22 10:12:29 2024 +0000 Auto merge of #12522 - rust-lang:Manishearth-patch-1, r=xFrednet Correct version for incompatible_msrv Unsure what happened here Probably what caused https://github.com/rust-lang/blog.rust-lang.org/issues/1277 changelog: none commit 7c9fe30ce4313cc9e77030ca1e66a7c88a8ffd02 Author: Samuel Tardieu Date: Fri Mar 22 00:33:31 2024 +0100 Do not warn on .map(_::clone) for Arc, Rc, and their weak variants Those constructions are idiomatic, and using `Arc::clone(x)` and `Rc::clone(x)` is often the recommended way of cloning a `Arc` or a `Rc`. commit fc0d51ae655dd17ffe73fdf0b9a4819d262a8e1b Merge: 5577612fd00 0e8170e8463 Author: bors Date: Fri Mar 22 07:45:50 2024 +0000 Auto merge of #16919 - roife:fix-issue-16800, r=Veykril fix: handle self::super when lowering UseTree fix #16800. commit 0e8170e846388d520b2242c01085c08eb59daa28 Author: roife Date: Fri Mar 22 15:36:00 2024 +0800 fix: handle self::super in lowering of UseTree commit 5577612fd0007e5eee9bd4cd8170dbc7cb8a92f7 Merge: 7ef7f442fc3 ea447062c4b Author: bors Date: Fri Mar 22 07:06:13 2024 +0000 Auto merge of #16918 - Veykril:utf8-paths, r=Veykril fix: Don't assert paths being utf8 when filtering them in the watcher Closes https://github.com/rust-lang/rust-analyzer/issues/16914 commit ea447062c4b3725a64558dd8b9b11eb223dc68d7 Author: Lukas Wirth Date: Fri Mar 22 08:04:50 2024 +0100 fix: Don't assert paths being utf8 when filtering them in the watcher commit b7026f87f52f75ebcb7c0de0c1bf56b714d4b73f Author: León Orell Valerian Liehr Date: Sun Feb 11 09:22:52 2024 +0100 Update (doc) comments Several (doc) comments were super outdated or didn't provide enough context. Some doc comments shoved everything in a single paragraph without respecting the fact that the first paragraph should be a single sentence because rustdoc treats these as item descriptions / synopses on module pages. commit 75390b9f7f2e25f0c3f831a63035bab7cfd98685 Author: León Orell Valerian Liehr Date: Fri Mar 15 03:21:55 2024 +0100 Rename AstConv to HIR ty lowering This includes updating astconv-related items and a few local variables. commit 28db4ccda76ffd2ce4c36912a194979a7ce2ef8d Author: Michael Howell Date: Thu Mar 21 17:19:39 2024 -0700 rustdoc-search: compressed bitmap to sort, then load desc This adds a bit more data than "pure sharding" by including information about which items have no description at all. This way, it can sort the results, then truncate, then finally download the description. With the "e" bitmap: 2380KiB Without the "e" bitmap: 2364KiB commit 0c392d918aae3dc8a2eedc9a26b1ca9d7ed2ea1d Author: Catherine <114838443+Centri3@users.noreply.github.com> Date: Tue Jun 20 16:34:24 2023 -0500 new lint `legacy_numeric_constants` commit 7c1be82cd9abe195205bee476c546a7645632af3 Author: 6d7a Date: Thu Mar 21 22:57:21 2024 +0100 fix: Prevent stack overflow in recursive const types In the evaluation of const values of recursive types certain declarations could cause an endless call-loop within the interpreter (hir-ty’s create_memory_map), which would lead to a stack overflow. This commit adds a check that prevents values that contain an address in their value (such as TyKind::Ref) from being allocated at the address they contain. The commit also adds a test for this edge case. commit 0e62b184353c0c76b5e908746c0dc99dea05f7a5 Author: Philipp Krones Date: Thu Mar 21 22:20:40 2024 +0100 Merge commit '9d6f41691ed9dbfaec2a2df2661c42451f2fe0d3' into clippy-subtree-update commit 15da6e735a43cd3b49847d4d484d6ec75b33dad6 Author: Kevin Reid Date: Thu Mar 21 14:06:45 2024 -0700 Mention `size_hint()` effect in `flat_map_option` lint documentation. commit 7ef7f442fc34b5eadb1c6ad6433bd6d0c51b056b Merge: 4d442d84d79 fe28e470cdf Author: bors Date: Thu Mar 21 20:29:02 2024 +0000 Auto merge of #16913 - Veykril:client-watched-files, r=Veykril fix: Some file watching related vfs fixes Fixes https://github.com/rust-lang/rust-analyzer/issues/15554, additionally it seems that client side file watching was broken on windows this entire time, this PR switches `DidChangeWatchedFilesRegistrationOptions` to use relative glob patterns which do work on windows in VSCode. commit fe28e470cdf7d864fb69411e780b1e9791d6742a Author: Lukas Wirth Date: Thu Mar 21 21:22:49 2024 +0100 Use relative glob patterns in DidChangeWatchedFilesRegistrationOptions commit 23613a9de71758553876dd7e7149de04b026dae5 Author: Lukas Wirth Date: Thu Mar 21 20:34:55 2024 +0100 fix: Some file watching related vfs fixes commit 4d442d84d7929e0d52c4ed536ecd2d1d493d11d4 Merge: 16c8deeb860 cd8eb0fe6d0 Author: bors Date: Thu Mar 21 19:11:12 2024 +0000 Auto merge of #16912 - Veykril:span-trait, r=Veykril internal: Remove span trait commit cd8eb0fe6d0ebab269bef6eca42d850866442b67 Author: Lukas Wirth Date: Thu Mar 21 20:08:30 2024 +0100 internal: Remove span trait commit 16c8deeb8607848c24f033c314162a10c61cafb0 Merge: a3d96253a91 c381d0fc64e Author: bors Date: Thu Mar 21 18:04:38 2024 +0000 Auto merge of #16835 - wyatt-herkamp:use_one_tt_for_a_derive, r=Veykril Have Derive Attribute share a token tree with it's proc macros. The goal of this PR is to stop creating a token tree for each derive proc macro. This is done by giving the derive proc macros an id to its parent derive element. From running the analysis stat on the rust analyzer project I did see a small memory decrease. ``` Inference: 42.80s, 362ginstr, 591mb MIR lowering: 8.67s, 67ginstr, 291mb Mir failed bodies: 18 (0%) Data layouts: 85.81ms, 609minstr, 8mb Failed data layouts: 135 (6%) Const evaluation: 440.57ms, 5235minstr, 13mb Failed const evals: 1 (0%) Total: 64.16s, 552ginstr, 1731mb ``` After Change ``` Inference: 40.32s, 340ginstr, 593mb MIR lowering: 7.95s, 62ginstr, 292mb Mir failed bodies: 18 (0%) Data layouts: 87.97ms, 591minstr, 8mb Failed data layouts: 135 (6%) Const evaluation: 433.38ms, 5226minstr, 14mb Failed const evals: 1 (0%) Total: 60.49s, 523ginstr, 1680mb ``` Currently this breaks the expansion for the actual derive attribute. ## TODO - [x] Pick a better name for the function `smart_macro_arg` commit c381d0fc64e02898ec77617a14e16431595b8906 Author: Wyatt Herkamp Date: Thu Mar 21 13:41:46 2024 -0400 Review Updates commit 262e06f1ef5114a636502a93a01aa7836dd3d45f Author: Wyatt Herkamp Date: Thu Mar 21 12:50:58 2024 -0400 Remove MacroCallKind::DeriveAttr commit a3d96253a91aed015d1dff03523c17e9c1023434 Merge: 20290b29922 2ad14b80692 Author: bors Date: Thu Mar 21 15:58:27 2024 +0000 Auto merge of #16911 - Veykril:rustc-crates, r=Veykril fix: Fix projects depending on `rustc_private` hanging If loading the root fails, we'll hang up in this loop as we never inserted the entry that asserts we already visited a package. This fixes that Fixes https://github.com/rust-lang/rust-analyzer/issues/16902 commit 2ad14b806924600eafef2cdb62ca59de16d54568 Author: Lukas Wirth Date: Thu Mar 21 16:55:42 2024 +0100 fix: Fix projects depending on rustc_private hanging commit bc0965e2ff3a44850983fddd5ade495711adbd8d Author: Michael Goulet Date: Wed Mar 20 16:53:50 2024 -0400 Implement macro-based deref!() syntax for deref patterns Stop using `box PAT` syntax for deref patterns, as it's misleading and also causes their semantics being tangled up. commit 87b93520a82719c1f2f3aba35dd432ededbcff99 Author: Manish Goregaokar Date: Thu Mar 21 15:41:12 2024 +0000 Correct version for incompatible_msrv Unsure what happened here Probably what caused https://github.com/rust-lang/blog.rust-lang.org/issues/1277 commit 20290b29922701f1f719d2a3c0151b8aef285224 Merge: 83f9cc677fc 0036762b9d3 Author: bors Date: Thu Mar 21 15:04:17 2024 +0000 Auto merge of #16909 - Veykril:spans, r=Veykril fix: Keep the span for `Attr::Literal` around commit c376addfcc46f7eeec02d765027c8febc17a5825 Author: Wyatt Herkamp Date: Thu Mar 21 08:12:26 2024 -0400 Cleanup commit 70f5344debc2d6ba0a24d832617e5415a9b0514c Author: Wyatt Herkamp Date: Mon Mar 18 13:20:16 2024 -0400 macro_arg_considering_derives is now in ExpandDatabase and now takes in the MacroCallKind commit 2c2bbe07fdc08b52dfb4e9cb47ac66ecd4276027 Author: Wyatt Herkamp Date: Mon Mar 18 07:10:02 2024 -0400 Treat Derive Macro specially. commit 4bd2f948bb9c95fae834111f1e68baa99640e527 Author: Wyatt Herkamp Date: Fri Mar 15 11:15:02 2024 -0400 Formatting commit f499564d0a39eca95b97c16bde8e34324225b1ab Author: Wyatt Herkamp Date: Thu Mar 14 09:49:00 2024 -0400 censor attribute derive commit 15d183be79f5d31a46f40b54a8308f13cf2879cd Author: Wyatt Herkamp Date: Thu Mar 14 08:52:13 2024 -0400 Initial Attempt limiting number of token tree in macro expansion. commit fb1b198ceae197cab272b58db28653cd390af345 Merge: e8794ff5417 9022122b8b3 Author: Matthias Krüger Date: Thu Mar 21 12:05:08 2024 +0100 Rollup merge of #122799 - estebank:issue-122569, r=fee1-dead Replace closures with `_` when suggesting fully qualified path for method call ``` error[E0283]: type annotations needed --> $DIR/into-inference-needs-type.rs:12:10 | LL | .into()?; | ^^^^ | = note: cannot satisfy `_: From<...>` = note: required for `FilterMap<...>` to implement `Into<_>` help: try using a fully qualified path to specify the expected types | LL ~ let list = , _>, _> as Into>::into(vec LL | .iter() LL | .map(|s| s.strip_prefix("t")) LL ~ .filter_map(Option::Some))?; | ``` Fix #122569. commit 83f9cc677fcf97a181a4478435ec87f7d58634eb Merge: 8d74705b430 6d1071962f1 Author: bors Date: Thu Mar 21 11:04:41 2024 +0000 Auto merge of #16895 - Veykril:spans, r=Veykril Resolve whether `$pat` is `$pat_param` or not via 🌟hygiene🌟 Before we just picked the edition from the macro def which is wrong, since a macro call can produce the fragment kind from a different definition site. commit 0036762b9d3823dacd3181fa03780b5ae9166613 Author: Lukas Wirth Date: Thu Mar 21 11:49:09 2024 +0100 Make use of `ThinArc` in `RawAttrs` commit 928d847cc2c19bf5157293baa1327593ab5034b6 Author: Lukas Wirth Date: Thu Mar 21 10:28:25 2024 +0100 Keep the span for Attr::Literal commit 6d1071962f17cb2f8ded66bae28b571c8f49f76b Author: Lukas Wirth Date: Tue Mar 19 19:48:09 2024 +0100 Resolve whether $pat is $pat_param or not via 🌟hygiene🌟 commit 7e88fa5d3a6914ae95b2c846f2ddb63a334576cd Author: Lukas Wirth Date: Tue Mar 19 17:06:50 2024 +0100 Remove span generics from most of the mbe crate commit 255a8aef92137195b803b5b5df22a4ebe49c2168 Author: Lukas Wirth Date: Tue Mar 19 16:53:34 2024 +0100 Move Edition into span crate commit 8d74705b4300620070dc5cbcf26b0238382ceb72 Merge: dc2e0b35cc1 79183394aaa Author: bors Date: Thu Mar 21 06:42:21 2024 +0000 Auto merge of #16904 - lnicola:bump-rust-cache, r=lnicola internal: Bump rust-cache action Fixes a Node 16 deprecation warning and also pulls in https://github.com/Swatinem/rust-cache/pull/147, which sounds interesting. commit 79183394aaac0855a1b5057c756c3ec2583e600f Author: Laurențiu Nicola Date: Thu Mar 21 08:34:18 2024 +0200 Bump rust-cache action commit dc2e0b35cc171543b4f330d44576e1bfe8481067 Merge: 5e276ae51c3 d6b0aae0190 Author: bors Date: Thu Mar 21 06:29:33 2024 +0000 Auto merge of #16891 - goodmost:master, r=lnicola chore: remove repetitive words commit e8794ff5417f91d23d3e62485c6e1778cbd34574 Merge: 54d1260174c 1dbabc1767d Author: bors Date: Thu Mar 21 06:12:24 2024 +0000 Auto merge of #121123 - compiler-errors:item-assumptions, r=oli-obk Split an item bounds and an item's super predicates This is the moral equivalent of #107614, but instead for predicates this applies to **item bounds**. This PR splits out the item bounds (i.e. *all* predicates that are assumed to hold for the alias) from the item *super predicates*, which are the subset of item bounds which share the same self type as the alias. ## Why? Much like #107614, there are places in the compiler where we *only* care about super-predicates, and considering predicates that possibly don't have anything to do with the alias is problematic. This includes things like closure signature inference (which is at its core searching for `Self: Fn(..)` style bounds), but also lints like `#[must_use]`, error reporting for aliases, computing type outlives predicates. Even in cases where considering all of the `item_bounds` doesn't lead to bugs, unnecessarily considering irrelevant bounds does lead to a regression (#121121) due to doing extra work in the solver. ## Example 1 - Trait Aliases This is best explored via an example: ``` type TAIT = impl TraitAlias; trait TraitAlias = A + B where T: C; ``` The item bounds list for `Tait` will include: * `Tait: A` * `Tait: B` * `T: C` While `item_super_predicates` query will include just the first two predicates. Side-note: You may wonder why `T: C` is included in the item bounds for `TAIT`? This is because when we elaborate `TraitAlias`, we will also elaborate all the predicates on the trait. ## Example 2 - Associated Type Bounds ``` type TAIT = impl Iterator; ``` The `item_bounds` list for `TAIT` will include: * `Tait: Iterator` * ` as Iterator>::Item: A` But the `item_super_predicates` will just include the first bound, since that's the only bound that is relevant to the *alias* itself. ## So what This leads to some diagnostics duplication just like #107614, but none of it will be user-facing. We only see it in the UI test suite because we explicitly disable diagnostic deduplication. Regarding naming, I went with `super_predicates` kind of arbitrarily; this can easily be changed, but I'd consider better names as long as we don't block this PR in perpetuity. commit 9022122b8b33e5a1fb9c07fcb3984005ef1a63e0 Author: Esteban Küber Date: Wed Mar 20 22:50:32 2024 +0000 Replace closures with `_` when suggesting fully qualified path for method call ``` error[E0283]: type annotations needed --> $DIR/into-inference-needs-type.rs:12:10 | LL | .into()?; | ^^^^ | = note: cannot satisfy `_: From<...>` = note: required for `FilterMap<...>` to implement `Into<_>` help: try using a fully qualified path to specify the expected types | LL ~ let list = , _>, _> as Into>::into(vec LL | .iter() LL | .map(|s| s.strip_prefix("t")) LL ~ .filter_map(Option::Some))?; | ``` Fix #122569. commit 1dbabc1767d080066ddf6f16f3aad01e46257d9c Author: Michael Goulet Date: Wed Mar 20 12:52:54 2024 -0400 Bless test fallout (duplicate diagnostics) commit 54d1260174c4c587fe56323459332f667978e8b4 Author: Guillaume Gomez Date: Wed Mar 20 16:47:11 2024 +0100 Rename `hir::Let` into `hir::LetExpr` commit 5e276ae51c35eaa22411ba81a4c00457f32e6ab5 Merge: 6fce1d71dfb c7f02012b1e Author: bors Date: Wed Mar 20 13:41:39 2024 +0000 Auto merge of #16899 - Veykril:discover-fix, r=Veykril fix: Fix project discovery not checking whether the `Cargo.toml` actually exists Got dropped in https://github.com/rust-lang/rust-analyzer/pull/16889, somehow r-a's codebase itself doesn't even run into this so I didn't see it when testing ... commit c7f02012b1e123665393ff0bc958c952fae46c89 Author: Lukas Wirth Date: Wed Mar 20 14:39:35 2024 +0100 fix: Fix project discovery not checking whether the `Cargo.toml` actually exists commit 6fce1d71dfb123e7a3e213a4f771f66d8bc66b3d Merge: 4e54b4bd6cb 7b91d013602 Author: bors Date: Tue Mar 19 15:36:26 2024 +0000 Auto merge of #16892 - Veykril:crate-graph-non-eager, r=Veykril internal: Don't eagerly try to read crate root file contents before VFS Fixes https://github.com/rust-lang/rust-analyzer/issues/8623 commit 7b91d01360288a5877fd3415a9e66d347681675b Author: Lukas Wirth Date: Tue Mar 19 16:12:56 2024 +0100 internal: Don't eagerly try to read crate root file contents before VFS commit 4e54b4bd6cb2f79a60ea78aad08f5091d59cf264 Merge: a2f73d31420 399dbc074b0 Author: bors Date: Tue Mar 19 14:57:18 2024 +0000 Auto merge of #16889 - Veykril:utf8-path, r=Veykril internal: Enforce utf8 paths Cargo already requires this, and I highly doubt r-a works with non-utf8 paths generally either. This just makes dealing with paths a lot easier. commit a2f73d31420d2a6566b3fb55311dec0ffd539b63 Merge: e03df77d04a 08a5f1e52ad Author: bors Date: Tue Mar 19 14:44:05 2024 +0000 Auto merge of #16879 - Nadrieril:fuel, r=Veykril Add fuel to match checking Exhaustiveness checking is NP-hard hence can take extremely long to check some specific matches. This PR makes ehxaustiveness bail after a set number of steps. I chose a bound that takes ~100ms on my machine, which should be more than enough for normal matches. I'd like someone with less recent hardware to run the test to see if that limit is low enough for them. Also curious if the r-a team thinks this is a good ballpark or if we should go lower/higher. I don't have much data on how complex real-life matches get, but we can definitely go lower than `500 000` steps. The second commit is a drive-by soundness fix which doesn't matter much today but will matter once `min_exhaustive_patterns` is stabilized. Fixes https://github.com/rust-lang/rust-analyzer/issues/9528 cc `@matklad` commit 399dbc074b0b2dff78a5f268aca7b6fe576d0545 Author: Lukas Wirth Date: Tue Mar 19 13:05:50 2024 +0100 internal: Enforce utf8 paths commit d6b0aae0190b738381f44aa15ddc5b81e6f08089 Author: goodmost Date: Tue Mar 19 22:14:13 2024 +0800 chore: remove repetitive words Signed-off-by: goodmost commit 08a5f1e52ad519766ef27c5f61d56c322a394cce Author: Nadrieril Date: Tue Mar 19 15:06:58 2024 +0100 Skip the test when testing locally commit e03df77d04a4a1b34c28210c635e8ff743c38778 Merge: b91697de8f3 967a864d036 Author: bors Date: Tue Mar 19 13:15:36 2024 +0000 Auto merge of #16812 - ShoyuVanilla:issue-3739, r=Veykril fix: Goto implementation to impls inside blocks Fixes #3739 commit 967a864d03686e07f7e3c17d4856201130f7cee3 Author: Shoyu Vanilla Date: Tue Mar 12 01:13:13 2024 +0900 fix: Goto implementation to impls inside blocks commit b91697de8f319c17650ddac01d98ac218f2ab617 Merge: 1c2d7d02ea2 ba339596bfb Author: bors Date: Tue Mar 19 10:39:54 2024 +0000 Auto merge of #16886 - Veykril:dev-deps-delay, r=Veykril internal: Delay drawing of workspace dev-dependency edges Follow up to https://github.com/rust-lang/rust-analyzer/pull/16871 With this we should prefer non-dev deps if they do form a cycle, https://github.com/rust-lang/rust-analyzer/issues/14167 commit ba339596bfba03b5f607f11a03e2c70ac650f7a7 Author: Lukas Wirth Date: Tue Mar 19 11:32:34 2024 +0100 internal: Delay drawing of workspace dev-dependency edges commit 1c2d7d02ea26316e3a35e4b0a9b1871a26c3514f Merge: 054ebf94824 b38d5394bb5 Author: bors Date: Tue Mar 19 10:11:42 2024 +0000 Auto merge of #16884 - Veykril:grammar-codegen, r=Veykril internal: Move grammar codegen into xtask https://github.com/rust-lang/rust-analyzer/issues/14778, also threw in the one line fix for https://github.com/rust-lang/rust-analyzer/issues/13912 commit 054ebf948248a8ad997a7c3b72c928abe369278b Merge: c626db048c0 dc4e5987a4c Author: bors Date: Tue Mar 19 09:58:31 2024 +0000 Auto merge of #16885 - Veykril:match-recovery, r=Veykril fix: Improve error recovery for match arms This should make use of the recovery token sets, but I think it'd be better to fix that as a whole while fixing the other places for these adhoc recovery checks. commit b38d5394bb53ffc9233322e5523c1f8a789dbd34 Author: Lukas Wirth Date: Tue Mar 19 10:40:36 2024 +0100 internal: Move grammar codegen into xtask commit dc4e5987a4c3bcfb45be3e2d264d17e8ac689df2 Author: Lukas Wirth Date: Tue Mar 19 10:55:22 2024 +0100 fix: Improve error recovery for match arms commit c626db048c0ee703fd2f1313ccb1c5f56a80fdc8 Merge: 4de0204d581 232125be128 Author: bors Date: Tue Mar 19 09:08:56 2024 +0000 Auto merge of #16883 - Veykril:progress-report, r=Veykril minor: Do progress reporting for crate-graph construction Also fixes https://github.com/rust-lang/rust-analyzer/issues/16828 commit 232125be1289d1b2273435e113299deff09bcea0 Author: Lukas Wirth Date: Tue Mar 19 10:06:51 2024 +0100 minor: Do progress reporting for crate-graph construction commit b357bcab2beb3533d3059b4c8723e4729a4333e2 Author: dfireBird Date: Tue Mar 19 10:14:45 2024 +0530 modify `insert_type_vars` for lifetimes commit 2f1ab2ce0925af91d252aea2608284c34faa0446 Author: Jules Bertholet Date: Mon Mar 18 23:07:28 2024 -0400 Reimplement `CaseMappingIter` with `core::array::IntoIter` Makes the iterator 2*usize larger, but I doubt that matters much. In exchange, we save a lot on instruction count. In the absence of delegation syntax, we must forward all the specialized impls manually… commit 3930f8b45d4ed1a6a51caed7efbcb7894f454e73 Author: y21 <30553356+y21@users.noreply.github.com> Date: Mon Mar 18 23:58:09 2024 +0100 fix infinite loop when peeling unwrap method calls commit e860b9cd24ba7555c0aba8e850985c30a837335e Author: Michael Howell Date: Mon Mar 18 13:39:35 2024 -0700 Use promise.all to load sorted results in parallel commit 4de0204d58125392aa14d4f224b29f3c54a274e5 Merge: 59b9cc17f9a 92300e8f865 Author: bors Date: Mon Mar 18 22:26:41 2024 +0000 Auto merge of #16880 - HKalbasi:test-explorer, r=HKalbasi Use `--workspace` and `--no-fail-fast` in test explorer This PR contains: * Using `--workspace` in `cargo test` command, to running all tests even when there is a crate in the root of a workspace * Using `--no-fail-fast` to run all requested tests * Excluding bench in the test explorer * Fixing a bug in the `hack_recover_crate_name` fix #16874 commit 92300e8f865e5e8418f06331198aa12858cd080a Author: hkalbasi Date: Tue Mar 19 01:46:41 2024 +0330 Use `--workspace` and `--no-fail-fast` in test explorer commit 59b9cc17f9a57176e21492e974e64a83b015166d Merge: d3eeadc242c 76fb73a99e2 Author: bors Date: Mon Mar 18 20:54:38 2024 +0000 Auto merge of #16871 - Veykril:dev-dependency-cycles, r=Veykril fix: Skip problematic cyclic dev-dependencies Implements a workaround for https://github.com/rust-lang/rust-analyzer/issues/14167, notably it does not implement the ideas surfaced in the issue, but takes a simpler to implement approach (and one that is more consistent). Effectively, all this does is discard dev-dependency edges that go from a workspace library target to another workspace library target. This means, using a dev-dependency to another workspace member inside unit tests will always fail to resolve for r-a now, (instead of being order dependent and causing problems elsewhere) while things will work out fine in integration tests, benches, examples etc. This effectively acknowledges package cycles to be okay, but crate graph cycles to be invalid: Quoting https://github.com/rust-lang/rust-analyzer/issues/14167#issuecomment-1864145772 > Though, if you have “package cycle” in integration tests, you’d have “crate cycle” in unit test. We disallow the latter here, while continuing to support the former (What's missing is to supress diagnostics for such unit tests, though not doing so might be a good deterrent, making devs avoid the pattern altogether) commit 040f37a99d430395b0d9ace6ea468f895f50fc16 Author: Nadrieril Date: Sun Mar 17 16:38:01 2024 +0100 Avoid hanging on complex matches commit e67adf40c98c95bb25a27a66c5b3f20a8094b296 Author: Nadrieril Date: Sun Mar 17 16:35:47 2024 +0100 Don't assume place validity when we don't know commit 3cfcd4ed962ffede6dc1405f26c34ac4b9c2462c Author: Nadrieril Date: Sun Mar 17 16:33:25 2024 +0100 Abstract over the uses of `compute_match_usefulness` commit b34afba5fbfd14cb9aa30bfc76fbfe700c5b4072 Author: xFrednet Date: Mon Mar 18 14:31:00 2024 +0100 Apply pretty review comments =^.^= commit 13301e7a1ae26214a038a6c344df9d85b895b540 Author: dfireBird Date: Mon Mar 18 15:48:59 2024 +0530 replace static_lifetime with new_lifetime_var where necessary implemented suggested fixes and changes and fix merge conflicts commit a555e95c9af0490e03c7714937667f99b7a5f388 Author: dfireBird Date: Wed Mar 13 21:07:20 2024 +0530 fix make HirDisplay format lifetimes first commit 8d08b337fa023a8db4fe6b8dd74eed0efea4e7f1 Author: dfireBird Date: Tue Mar 12 00:05:37 2024 +0530 include lifetime in the filter for data layout in analysis-stats clippy fixes commit 490391f576f2d2660537ba0d801f708fd99745cb Author: dfireBird Date: Mon Mar 11 10:03:40 2024 +0530 fix HirDisplay inserting anonymous lifetimes and update tests commit 0669ae7fafea27f63f4ed36fb788345d9b6128d4 Author: dfireBird Date: Mon Mar 11 02:06:47 2024 +0530 handle lifetimes separately in substs function commit a6c8cbfd9116226541e91528e6a20c26d52ead46 Author: dfireBird Date: Sat Mar 9 01:08:35 2024 +0530 update `Generics` iter methods to return `GenericParamId` commit d6e3929841cbf78adff4e2edb0f6005919ad3a35 Author: dfireBird Date: Fri Mar 8 18:05:44 2024 +0530 include lifetime in ParamKind and in Generics::provenance_split commit e463a3e1cf5041581bdc8ede2488d60a1ef638ae Author: dfireBird Date: Fri Mar 8 18:00:07 2024 +0530 update `make_binders` to include lifetimes in generics adds a bunch of iter methods that include lifetimes commit 16493e301ee6eca66ce8806c2e92139e7a964193 Author: dfireBird Date: Wed Mar 6 23:15:04 2024 +0530 fix index returned for the use of BoundVar commit f95b3d4cd206f9ffc5a7a937e66afb6dacb75867 Author: dfireBird Date: Wed Mar 6 16:39:24 2024 +0530 implement resolving and lowering of Lifetimes commit d3eeadc242cffece74f8ff5e24029b6881b25a70 Merge: 7c2bb75bc85 d034ab0f925 Author: bors Date: Mon Mar 18 10:38:24 2024 +0000 Auto merge of #16852 - ShoyuVanilla:atpit, r=Veykril feat: Implement ATPIT Resolves #16584 Note: This implementation only works for ATPIT, not for TAIT. The main hinderence that blocks the later is the defining sites of TAIT can be inner blocks like in; ```rust type X = impl Default; mod foo { fn bar() -> super::X { () } } ``` So, to figure out we are defining it or not, we should recursively probe for nested modules and bodies. For ATPIT, we can just look into current body because `error[E0401]: can't use 'Self' from outer item` prevent such nested structures; ```rust trait Foo { type Item; fn foo() -> Self::Item; } struct Bar; impl Foo for Bar { type Item = impl Default; fn foo() -> Self::Item { fn bar() -> Self::Item { ^^^^^^^^^^ | use of `Self` from outer item refer to the type directly here instead 5 } bar() } } ``` But this implementation does not checks for unification of same ATPIT between different bodies, monomorphization, nor layout for similar reason. (But these can be done with lazyness if we can utilize something like "mutation of interned value" with `db`. I coundn't find such thing but I would appreciate it if such thing exists and you could let me know 😅) commit 76fb73a99e2794b0e67ff279e48a3f88c193d41a Author: Lukas Wirth Date: Mon Mar 18 11:14:36 2024 +0100 Skip problematic cyclic dev-dependencies commit d034ab0f925bcbfaf5e66e861dfc816748722ff2 Author: Shoyu Vanilla Date: Mon Mar 18 18:25:41 2024 +0900 Apply reviewed suggestions commit 7c2bb75bc85d94d075e7f6ce8c1a9fdce2c77e45 Merge: f40c7d8a9ce 4b679f90dd2 Author: bors Date: Mon Mar 18 09:14:08 2024 +0000 Auto merge of #16860 - Veykril:macarons, r=Veykril feat: Syntax highlighting improvements Specifically - Adds a new `constant` modifier, attached to keyword `const` (except for `*const ()` and `&raw const ()`), `const` items and `const` functions - Adds (or rather reveals) `associated` modifier for associated items - Fixes usage of the standard `static` modifier, now it acts like `associated` except being omitted for methods. - Splits `SymbolKind::Function` into `Function` and `Method`. We already split other things like that (notable self param from params), so the split makes sense in general as a lot special cases around it anyways. commit f40c7d8a9ce0dd7529a776133ec8f0636403cfa5 Merge: f6e2895ee66 4a93368590a Author: bors Date: Mon Mar 18 09:00:59 2024 +0000 Auto merge of #16822 - Veykril:inlays, r=Veykril fix: Make inlay hint resolving work better for inlays targetting the same position commit 4a93368590a71b1cb17d319aa0343d7def68737b Author: Lukas Wirth Date: Tue Mar 12 15:41:51 2024 +0100 Use a hash to find the correct inlay hint when resolving commit 3115fd8b41f2a920e0498a8bfe326e976d5978c6 Author: Lukas Wirth Date: Tue Mar 12 15:02:17 2024 +0100 Simplify inlay hints `needs_resolve` commit f6e2895ee667b0c65b022d67dee6287be65edf37 Merge: a71a0328d88 4422a90b110 Author: bors Date: Mon Mar 18 08:48:57 2024 +0000 Auto merge of #16839 - Wilfred:extension_refactor_for_shell, r=Veykril Refactor extension to support arbitrary shell command runnables Currently, the extension assumes that all runnables invoke cargo. Arguments are sometimes full CLI arguments, and sometimes arguments passed to a cargo subcommand. Refactor the extension so that tasks are just a `program` and a list of strings `args`, and rename `CargoTask` to `RustTask` to make it generic. (This was factored out of #16135 and tidied.) commit a71a0328d8819590edb636426cacc1f847f13adc Merge: f07489ada99 95828850b21 Author: bors Date: Mon Mar 18 08:35:53 2024 +0000 Auto merge of #16830 - Jesse-Bakker:fix-ty-panic, r=ShoyuVanilla Fix panic with impl trait associated types in where clause Not sure if this is the correct fix, but the tests are green :') Fixes #16823 commit 4b679f90dd226daf68042d9e956a819a5d57f5ed Author: Lukas Wirth Date: Sun Mar 17 11:51:41 2024 +0100 Generate AST in a more stable manner commit f07489ada9920c83a16a71667d4792287e137205 Merge: 65c601fa422 109344cfb75 Author: bors Date: Mon Mar 18 08:22:26 2024 +0000 Auto merge of #16868 - roife:fix-issue-16848, r=Veykril fix: handle attributes when typing curly bracket fix #16848. When inserting a `{`, if it is identified that the front part of `expr` is `attr`, we consider it as inserting `{}` around the entire `expr` (excluding the attr part). commit 65c601fa422f92ae1932d70130a9b89d9fb6869d Merge: b6d1887bc4f 8d59aaf7350 Author: bors Date: Mon Mar 18 08:08:11 2024 +0000 Auto merge of #16863 - Nadrieril:update-pat-ana, r=Veykril Bump dependencies and use in-tree `rustc_pattern_analysis` One last `pattern_analysis` API change. I don't have any more planned! So we can now use the in-tree version when available. commit 109344cfb756cabbafccd201b0e658e6a7287f05 Author: roife Date: Mon Mar 18 13:19:24 2024 +0800 fix: handle attributes when typing curly bracket commit 2e368bfe9ec1ace30b43673d8b30a7f388b86e28 Author: Michael Howell Date: Sat Mar 16 23:01:47 2024 -0700 Fix style errors commit b6d1887bc4f9543b6c6bf098179a62446f34a6c3 Merge: c1122c9eebc bb541c38d33 Author: bors Date: Sun Mar 17 20:41:40 2024 +0000 Auto merge of #16861 - Veykril:macro-diag-exceptions, r=Veykril fix: Ignore some warnings if they originate from within macro expansions These tend to be annoying noise as we can't handle `allow`s for them properly for the time being. commit bb541c38d33de08d98f8bec7c013c1141695e707 Author: Lukas Wirth Date: Sun Mar 17 12:03:24 2024 +0100 fix: Ignore some warnings if they originate from within macro expansions commit c1122c9eebca8b9c15759502f99b6d96a5dda3d1 Merge: 3d39ddf2556 2a8edaa14db Author: bors Date: Sun Mar 17 20:28:52 2024 +0000 Auto merge of #16862 - matthiaskrgr:noclone, r=Veykril internal: remove redundant clone()s commit dec81ac223aeda234e72de0eb70443153ef67e96 Author: Erik Desjardins Date: Sun Mar 17 13:40:01 2024 -0400 disable crashing test on sparc commit 6577aefc6f3863607b0d90d836d594a1887a0d90 Author: Erik Desjardins Date: Sun Mar 17 13:33:59 2024 -0400 Revert "sparc64: fix crash in ABI code for { f64, f32 } struct" This reverts commit 41c6fa812b0bed63e54c455134734452f9cee97c. commit 3d39ddf2556535b16c69bafd077ccb6fcc953b1e Merge: 5ecace48f69 405a62615d2 Author: bors Date: Sun Mar 17 15:56:41 2024 +0000 Auto merge of #16864 - lnicola:sync-from-rust, r=lnicola internal: Sync from downstream commit 405a62615d2e60813ad3054d8cbe43e17720337f Merge: 5a95a53a390 5ecace48f69 Author: Laurențiu Nicola Date: Sun Mar 17 17:52:56 2024 +0200 Merge branch 'master' into sync-from-rust commit 8d59aaf735020434a9448742c77f745acb92f60f Author: Nadrieril Date: Sun Mar 17 14:16:30 2024 +0100 Use in-tree rustc_pattern_analysis commit b99618c1916c24aaab70926218facedbfa8ad8aa Author: Nadrieril Date: Sun Mar 17 14:08:34 2024 +0100 Bump dependencies commit 2a8edaa14dbeb1f06f05d5e9fb842a8edfc46574 Author: Matthias Krüger Date: Sun Mar 17 12:50:22 2024 +0100 remove redundant clone()s commit 20d521db7484cf8ca7939d56493fac7800c444f5 Author: Lukas Wirth Date: Sun Mar 17 11:27:39 2024 +0100 Adjust `benchmark_syntax_highlighting_parser` for changes commit 77607ab99a586ec680ba494b922fdfecde10dbca Author: Lukas Wirth Date: Sun Mar 17 11:26:05 2024 +0100 More precise highlighting rules for constant modifier commit 640c8b722e8c91863d19fc2879ce69cfe16b3aa3 Author: Lukas Wirth Date: Sun Mar 17 11:10:29 2024 +0100 Move custom async modifier to standard one commit 23fff55f0c6b56eaf40395495e49375bd32cc123 Author: Lukas Wirth Date: Sun Mar 17 11:07:22 2024 +0100 Split SymbolKind::Function into Function and Method commit c8f6655327c1664ebbf20d86206e6028c6680944 Author: Lukas Wirth Date: Sun Mar 17 10:40:32 2024 +0100 Don't emit modifiers depending on the symbol kind in lsp conversion layer commit 66adc1cc942d5cc693525cf2cf14ab96f2371dd8 Author: Lukas Wirth Date: Sun Mar 17 10:35:33 2024 +0100 Emit `Const` modifier in syntax highlighting for const-like things commit 5a95a53a390d214cfc1e3e6797ddb7214b4a440a Author: Laurențiu Nicola Date: Sun Mar 17 11:04:52 2024 +0200 Merge commit '5ecace48f693afaa6adf8cb23086b651db3aec96' into sync-from-ra commit 5b44bfda7fc62b2874400e613672aefe5b49aaaa Author: Michael Howell Date: Sat Mar 16 17:50:44 2024 -0700 rustdoc-search: shard the search result descriptions The descriptions are, on almost all crates[^1], the majority of the size of the search index, even though they aren't really used for searching. This makes it relatively easy to separate them into their own files. This commit also bumps us to ES8. Out of the browsers we support, all of them support async functions according to caniuse. https://caniuse.com/async-functions [^1]: , a crate with 44MiB of pure names and no descriptions for them, is an outlier and should not be counted. commit 8d5fd94e6292b298e59a637d84fa16bed30d64d4 Author: Erik Desjardins Date: Sun Mar 17 00:25:36 2024 -0400 add tests for PassMode::Cast fixes Tests added in cast-target-abi.rs, covering the single element, array, and prefix cases in `CastTarget::llvm_type`, and the Rust-is-larger/smaller cases in the Rust<->ABI copying code. ffi-out-of-bounds-loads.rs was overhauled to be runnable on any platform. Its alignment also increases due to the removal of a `min` in the previous commit; this was probably an insufficient workaround for this issue or similar. The higher alignment is fine, since the alloca is actually aligned to 8 bytes, as the test checks now confirm. commit 8841315d3e9aa02cb54d757cb5cd8131c263a893 Author: Erik Desjardins Date: Sun Mar 17 00:22:35 2024 -0400 make PassMode::Cast consistently copy between Rust/ABI representation Previously, we did this slightly incorrectly for return values, and didn't do it at all for arguments. commit 74ef47e90ca1466ea7f3dd8c3ec3738f05345809 Author: Erik Desjardins Date: Sun Mar 17 00:14:20 2024 -0400 make CastTarget::size and CastTarget::llvm_type consistent, remove special case that's not present in Clang Making the methods consistent doesn't require much justification. It's required for us to generate correct code. The special case was present near the end of `CastTarget::llvm_type`, and resulted in the final integer component of the ABI type being shrunk to the smallest integer that fits. You can see this in action here (https://godbolt.org/z/Pe73cr91d), where, for a struct with 5 u16 elements, rustc generates `{ i64, i16 }`, while Clang generates `[2 x i64]`. This special case was added a long time ago, when the function was originally written [1]. That commit consolidated logic from many backends, and in some of the code it deleted, sparc64 [2] and powerpc64 [3] had similar special cases. However, looking at Clang today, it doesn't have this special case for sparc64 (https://godbolt.org/z/YaafvYWdf) or powerpc64 (https://godbolt.org/z/5c3YePTje), so this change just removes it. [1]: https://github.com/rust-lang/rust/commit/f0636b61c7f84962a609e831760db9d77f4f5e14#diff-183c4dadf10704bd1f521b71f71d89bf755c9603a93f894d66c03bb1effc6021R231 [2]: https://github.com/rust-lang/rust/commit/f0636b61c7f84962a609e831760db9d77f4f5e14#diff-2d8f87ea6db6d7f0a6fbeb1d5549adc07e93331278d951a1e051a40f92914436L163-L166 [3]: https://github.com/rust-lang/rust/commit/f0636b61c7f84962a609e831760db9d77f4f5e14#diff-88af4a9df9ead503a5c7774a0455d270dea3ba60e9b0ec1ce550b4c53d3bce3bL172-L175 commit 41c6fa812b0bed63e54c455134734452f9cee97c Author: Erik Desjardins Date: Sun Mar 17 00:11:54 2024 -0400 sparc64: fix crash in ABI code for { f64, f32 } struct This would trigger a `Size::sub: 0 - 8 would result in negative size` abort, if `data.last_offset > offset`. This is almost hilariously easy to trigger (https://godbolt.org/z/8rbv57xET): ```rust #[repr(C)] pub struct DoubleFloat { f: f64, g: f32, } #[no_mangle] pub extern "C" fn foo(x: DoubleFloat) {} ``` Tests for this will be covered by the cast-target-abi.rs test added in a later commit. commit 4498cd6a8da86e4d7a84953c891281c8c2dace26 Author: Erik Desjardins Date: Sun Mar 17 00:07:42 2024 -0400 extend extern tests to include FiveU16s As described in the code, this extends just beyond a 64bit reg, but isn't a round number, so it triggers some edge cases in the cast ABI. commit 1c137b7582948fe35d040cd2f805b90284046951 Author: Jules Bertholet Date: Sat Mar 16 23:27:50 2024 -0400 Optimize `core::char::CaseMappingIter` layout Godbolt says this saves a few instructions… commit f9a9c4bf3bc8c8675ef12c6f3580aea6a9df667c Author: 许杰友 Jieyou Xu (Joe) Date: Sat Mar 9 20:32:36 2024 +0000 Note that type param is chosen by caller when suggesting return impl Trait commit 351890d682241e4eb9b8a63cddfb22e90567b870 Author: Michael Howell Date: Fri Mar 15 17:49:23 2024 -0700 rustdoc: clean up formatting commit 50392ccc5a38f2e4580c67d51836f5add64f7d48 Author: Scott McMurray Date: Fri Mar 15 13:09:04 2024 -0700 Workaround issue 122566 commit 79766577f2206306e342c8adae1c95f8ab0b9413 Author: Guillaume Gomez Date: Wed Feb 7 22:01:18 2024 +0100 Add `missing_transmute_annotation` lint commit fc53c59388ea319a37cc599b1cdeef6a0f4f5ef1 Author: Shoyu Vanilla Date: Sat Mar 16 03:53:55 2024 +0900 fix: typo commit d2aba91a0c58111b9a5df1e2a1f26b8caf45be2e Author: Shoyu Vanilla Date: Sat Mar 16 03:31:12 2024 +0900 feat: Implement ATPIT commit 8a5245e7dd006e2eb6bf6d3834b05772285efe28 Author: Steven Trotter Date: Mon Feb 26 21:00:35 2024 +0000 Refactored a few bits: - Firstly get all the information about generics matching out of the HIR - Secondly the labelling for the function is more coherent now - Lastly a few error message improvements commit df933640571861bcc2854431823cd9d4803b9f88 Author: Steven Trotter Date: Sun Feb 25 16:36:26 2024 +0000 Added ability to report on generic argument mismatch better Needs some checking over and some tests have altered that need sanity checking, but overall this is starting to get somewhere now. commit 9eb26d734e94c6719e286ff85c29eacf0f11976d Author: Scott McMurray Date: Thu Mar 14 17:30:36 2024 -0700 Remove `feature(unchecked_math)` from miri commit 234e383c3426281447da140e24ccf6bf5402f6f5 Author: Scott McMurray Date: Thu Mar 14 15:08:53 2024 -0700 Stabilize `unchecked_{add,sub,mul}` commit 4422a90b1106065146d0f3f6d0aa86c212753c10 Author: Wilfred Hughes Date: Wed Mar 13 17:13:26 2024 -0700 refactor: Store the CLI command directly in RustTargetDefinition commit 2e109c7da8fb5b4adc809f30035472512e7ac7cd Author: Wilfred Hughes Date: Wed Mar 13 16:46:57 2024 -0700 refactor: Use a single CLI args array rather than a separate subcommand field commit d472fd932b8292479547166ca83b2e2425f57ccc Author: Wilfred Hughes Date: Wed Mar 13 15:47:34 2024 -0700 refactor: Rename CargoTask to RustTask in extension commit 4cbe42e00df7ebc951b6ba2152dc0eb27b449cb9 Author: xFrednet Date: Mon Mar 11 23:32:35 2024 +0100 RFC: Document Clippy's teams and team duties I want to be clear: this is just the initial draft outlining what, I think, should be the responsibilities of the team members. It has not yet been discussed with anyone else. commit 95828850b2117cda5c6766f6af862f955a4b8382 Author: Jesse Bakker Date: Wed Mar 13 17:59:27 2024 +0100 Fix panic with impl trait associated types in where clause commit 5af8187325210e3006874b1457fa6ec69293f0e9 Author: Alex Crichton Date: Tue Mar 12 15:10:40 2024 -0700 Provide `cabi_realloc` on `wasm32-wasip2` by default This commit provides a component model intrinsic in the standard library by default on the `wasm32-wasip2` target. This intrinsic is not required by the component model itself but is quite common to use, for example it's needed if a wasm module receives a string or a list. The intention of this commit is to provide an overridable definition in the standard library through a weak definition of this function. That means that downstream crates can provide their own customized and more specific versions if they'd like, but the standard library's version should suffice for general-purpose use. commit 3311283f90dd62dcd322d6730f0dd09b3ee6d7fd Author: Alex Crichton Date: Tue Mar 12 15:10:01 2024 -0700 Get wasm32-wasip2 compiling with its custom pal implementation The ordering of targets in `pal/mod.rs` did not end up using the wasip2 implementation, so after reordering that I've edited the implementation to compile correctly. commit ec313d1edb83c7b020e590bcbafa8cd19c94e0e9 Author: DianQK Date: Sat Mar 9 14:43:38 2024 +0800 Update the test case for `SimplifyCfg-after-unreachable-enum-branching` commit 102bda49b1889cf7c42d9338b797d301f54256a5 Author: DianQK Date: Sat Mar 9 11:58:36 2024 +0800 Remove restrictions on small enum statements such as `Order`, `Option` or `Result` `early-tailduplication` is only a problem when there are a significant number of branches. commit f8656ef6e9f4d5f2a05c1a76af21bd201eebd123 Author: DianQK Date: Sat Mar 9 11:45:29 2024 +0800 Update `unreachable_enum_default_branch.rs` commit a10d157f981bcb617e586d7b72d3342fe0acc57e Author: DianQK Date: Fri Mar 8 21:55:22 2024 +0800 Addition of parentheses to clarify precedence commit df33e02f4096d6d919b95754595295ceed3a40ae Author: DianQK Date: Fri Mar 8 22:01:36 2024 +0800 Add comments for `UnreachableEnumBranching` commit cbbb0ae7d2c430327e77aec5ac1235014a09b793 Author: Arthur Carcano Date: Fri Jan 5 16:13:24 2024 +0100 fix: allow-one-hash-in-raw-strings option of needless_raw_string_hashes was ignored Fixes: https://github.com/rust-lang/rust-clippy/issues/11481 changelog: Fix `allow-one-hash-in-raw-strings` option of [`needless_raw_string_hashes`] was ignored commit 6a16638de6449fd30b804e55f3cc2c142e32f55e Author: David Carlier Date: Tue Mar 12 00:33:48 2024 +0000 std::rand: fix dragonflybsd after #121942. commit 0f5140e3834c9af2b077a9cb3a782341ab149a85 Author: Guillaume Gomez Date: Mon Mar 11 16:27:44 2024 +0100 Use published gccjit dependency instead of git repository commit fa418487e0faf81d60c939494305368bcb818a90 Author: Guillaume Gomez Date: Mon Mar 11 13:26:18 2024 +0100 Vendor rustc_codegen_gcc commit 8ddd9662233d468052f5592d048d6939b4ddae55 Author: DianQK Date: Fri Mar 8 20:56:09 2024 +0800 Rename `UninhabitedEnumBranching` to `UnreachableEnumBranching` commit c36d5e3280fbb8001a4c506f2c21227156cb3771 Author: Ross Smyth Date: Sat Feb 17 12:43:54 2024 -0500 Add MatchKind member to the Match expr for pretty printing & fmt commit a50490c5793516acb03c7b27d26177137b3f16f5 Author: Michael Woerister Date: Fri Mar 1 14:12:11 2024 +0100 Use FxIndexMap instead FxHashMap to stabilize iteration order in EffectiveVisibilities. Part of https://github.com/rust-lang/compiler-team/issues/533 --- Cargo.lock | 53 +- README.md | 39 +- RELEASES.md | 10 + compiler/rustc_abi/src/lib.rs | 7 +- compiler/rustc_ast/src/ast.rs | 32 +- compiler/rustc_ast/src/ptr.rs | 16 +- compiler/rustc_ast/src/token.rs | 2 +- compiler/rustc_ast/src/tokenstream.rs | 2 +- compiler/rustc_ast/src/util/parser.rs | 7 +- compiler/rustc_ast_lowering/src/index.rs | 15 +- compiler/rustc_ast_lowering/src/item.rs | 4 +- compiler/rustc_ast_lowering/src/lib.rs | 12 +- compiler/rustc_ast_passes/messages.ftl | 2 +- .../rustc_ast_passes/src/ast_validation.rs | 31 +- compiler/rustc_ast_passes/src/errors.rs | 3 +- compiler/rustc_ast_passes/src/feature_gate.rs | 1 + compiler/rustc_ast_pretty/src/pprust/state.rs | 9 +- compiler/rustc_attr/Cargo.toml | 1 + compiler/rustc_attr/src/builtin.rs | 19 +- compiler/rustc_borrowck/src/borrow_set.rs | 2 +- .../src/diagnostics/conflict_errors.rs | 20 +- .../src/diagnostics/mutability_errors.rs | 34 +- .../src/type_check/liveness/polonius.rs | 2 + .../src/type_check/liveness/trace.rs | 9 +- compiler/rustc_borrowck/src/type_check/mod.rs | 8 +- compiler/rustc_builtin_macros/messages.ftl | 2 + compiler/rustc_builtin_macros/src/asm.rs | 6 +- compiler/rustc_builtin_macros/src/assert.rs | 2 +- compiler/rustc_builtin_macros/src/cfg.rs | 2 +- .../src/cfg_accessible.rs | 2 +- .../rustc_builtin_macros/src/concat_bytes.rs | 4 +- .../src/deriving/bounds.rs | 4 +- .../src/deriving/clone.rs | 8 +- .../src/deriving/cmp/eq.rs | 4 +- .../src/deriving/cmp/ord.rs | 4 +- .../src/deriving/cmp/partial_eq.rs | 4 +- .../src/deriving/cmp/partial_ord.rs | 4 +- .../src/deriving/debug.rs | 6 +- .../src/deriving/decodable.rs | 8 +- .../src/deriving/default.rs | 10 +- .../src/deriving/encodable.rs | 4 +- .../src/deriving/generic/mod.rs | 40 +- .../rustc_builtin_macros/src/deriving/hash.rs | 8 +- .../rustc_builtin_macros/src/deriving/mod.rs | 4 +- .../rustc_builtin_macros/src/edition_panic.rs | 2 +- compiler/rustc_builtin_macros/src/env.rs | 51 +- compiler/rustc_builtin_macros/src/errors.rs | 8 + compiler/rustc_builtin_macros/src/format.rs | 8 +- .../rustc_builtin_macros/src/source_util.rs | 2 +- compiler/rustc_builtin_macros/src/test.rs | 2 +- compiler/rustc_codegen_cranelift/.cirrus.yml | 3 + .../.github/workflows/abi-cafe.yml | 2 + .../.github/workflows/main.yml | 68 +- .../.github/workflows/rustc.yml | 2 + compiler/rustc_codegen_cranelift/Readme.md | 22 +- .../build_system/build_backend.rs | 16 +- .../build_system/build_sysroot.rs | 4 +- .../build_system/main.rs | 13 +- .../build_system/prepare.rs | 1 - .../build_system/tests.rs | 17 +- .../build_system/utils.rs | 18 - .../docs/rustc_testing.md | 23 + .../example/mini_core.rs | 32 +- .../rustc_codegen_cranelift/rust-toolchain | 2 +- .../scripts/filter_profile.rs | 9 +- compiler/rustc_codegen_cranelift/src/base.rs | 28 +- .../src/codegen_i128.rs | 3 +- .../src/debuginfo/line_info.rs | 15 +- .../src/debuginfo/mod.rs | 24 +- .../src/intrinsics/llvm_x86.rs | 2 +- .../src/intrinsics/simd.rs | 2 +- compiler/rustc_codegen_cranelift/src/num.rs | 26 +- .../rustc_codegen_cranelift/src/vtable.rs | 2 +- compiler/rustc_codegen_gcc/Cargo.lock | 10 +- compiler/rustc_codegen_gcc/Cargo.toml | 2 +- .../rustc_codegen_gcc/example/mini_core.rs | 30 + ...0001-core-Disable-portable-simd-test.patch | 4 +- compiler/rustc_codegen_gcc/src/common.rs | 4 + compiler/rustc_codegen_llvm/src/abi.rs | 120 +- compiler/rustc_codegen_llvm/src/attributes.rs | 2 +- compiler/rustc_codegen_llvm/src/back/write.rs | 18 +- compiler/rustc_codegen_llvm/src/common.rs | 4 + .../src/coverageinfo/mapgen.rs | 37 +- .../src/coverageinfo/mod.rs | 9 +- .../src/debuginfo/metadata.rs | 50 +- compiler/rustc_codegen_llvm/src/declare.rs | 56 +- compiler/rustc_codegen_llvm/src/intrinsic.rs | 4 +- compiler/rustc_codegen_llvm/src/llvm/mod.rs | 5 +- compiler/rustc_codegen_ssa/src/back/link.rs | 4 +- compiler/rustc_codegen_ssa/src/back/rpath.rs | 8 +- compiler/rustc_codegen_ssa/src/base.rs | 35 +- compiler/rustc_codegen_ssa/src/common.rs | 41 +- compiler/rustc_codegen_ssa/src/mir/block.rs | 38 +- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 46 +- .../rustc_codegen_ssa/src/traits/consts.rs | 1 + compiler/rustc_const_eval/messages.ftl | 1 + compiler/rustc_const_eval/src/errors.rs | 7 + .../rustc_const_eval/src/interpret/cast.rs | 12 +- .../rustc_const_eval/src/interpret/intern.rs | 17 +- .../rustc_const_eval/src/interpret/operand.rs | 9 +- .../src/interpret/operator.rs | 18 + .../rustc_const_eval/src/interpret/place.rs | 2 +- .../src/transform/check_consts/check.rs | 4 +- .../src/transform/validate.rs | 13 +- compiler/rustc_const_eval/src/util/mod.rs | 4 +- .../src/graph/dominators/mod.rs | 8 +- .../src/stable_hasher.rs | 48 +- .../src/tagged_ptr/copy.rs | 1 + compiler/rustc_errors/src/lib.rs | 37 +- compiler/rustc_expand/src/build.rs | 40 +- compiler/rustc_expand/src/expand.rs | 4 +- compiler/rustc_expand/src/mbe/macro_parser.rs | 2 +- compiler/rustc_feature/src/builtin_attrs.rs | 4 + compiler/rustc_feature/src/unstable.rs | 4 + compiler/rustc_hir/src/definitions.rs | 9 +- compiler/rustc_hir/src/hir.rs | 11 +- compiler/rustc_hir/src/hir_id.rs | 6 +- compiler/rustc_hir/src/lang_items.rs | 20 + compiler/rustc_hir_analysis/messages.ftl | 2 + compiler/rustc_hir_analysis/src/bounds.rs | 10 +- .../rustc_hir_analysis/src/check/check.rs | 4 +- compiler/rustc_hir_analysis/src/check/errs.rs | 42 +- .../rustc_hir_analysis/src/check/intrinsic.rs | 20 +- .../src/check/intrinsicck.rs | 4 +- .../rustc_hir_analysis/src/check/region.rs | 2 +- .../src/coherence/orphan.rs | 160 +- .../src/collect/resolve_bound_vars.rs | 8 +- .../rustc_hir_analysis/src/collect/type_of.rs | 4 +- compiler/rustc_hir_analysis/src/errors.rs | 47 +- .../src/hir_ty_lowering/errors.rs | 390 +- .../src/hir_ty_lowering/mod.rs | 357 +- .../src/hir_ty_lowering/object_safety.rs | 89 +- compiler/rustc_hir_pretty/src/lib.rs | 9 +- compiler/rustc_hir_typeck/messages.ftl | 4 +- compiler/rustc_hir_typeck/src/callee.rs | 3 +- compiler/rustc_hir_typeck/src/check.rs | 2 +- compiler/rustc_hir_typeck/src/closure.rs | 32 +- compiler/rustc_hir_typeck/src/demand.rs | 4 +- compiler/rustc_hir_typeck/src/expr.rs | 26 +- .../rustc_hir_typeck/src/expr_use_visitor.rs | 6 +- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 9 +- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 339 +- compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs | 10 +- compiler/rustc_hir_typeck/src/intrinsicck.rs | 2 +- .../src/mem_categorization.rs | 2 +- .../rustc_hir_typeck/src/method/suggest.rs | 2 +- compiler/rustc_hir_typeck/src/pat.rs | 143 +- compiler/rustc_hir_typeck/src/upvar.rs | 60 +- compiler/rustc_hir_typeck/src/writeback.rs | 9 + .../rustc_incremental/src/persist/load.rs | 3 +- .../rustc_incremental/src/persist/save.rs | 3 +- compiler/rustc_index/src/bit_set.rs | 2 +- compiler/rustc_index_macros/src/newtype.rs | 3 + compiler/rustc_infer/src/infer/mod.rs | 10 +- .../rustc_infer/src/infer/snapshot/fudge.rs | 6 +- compiler/rustc_infer/src/lib.rs | 2 +- compiler/rustc_infer/src/traits/mod.rs | 2 +- compiler/rustc_interface/src/passes.rs | 3 - compiler/rustc_interface/src/tests.rs | 1 - compiler/rustc_lint/src/lints.rs | 28 +- compiler/rustc_lint/src/types.rs | 96 +- compiler/rustc_lint_defs/src/builtin.rs | 12 +- compiler/rustc_llvm/build.rs | 6 + .../llvm-wrapper/CoverageMappingWrapper.cpp | 53 +- compiler/rustc_log/Cargo.toml | 2 +- compiler/rustc_metadata/src/rmeta/encoder.rs | 16 +- compiler/rustc_middle/src/arena.rs | 1 - .../rustc_middle/src/dep_graph/dep_node.rs | 14 +- compiler/rustc_middle/src/hir/map/mod.rs | 5 +- compiler/rustc_middle/src/hir/mod.rs | 8 +- compiler/rustc_middle/src/infer/canonical.rs | 2 +- .../src/middle/codegen_fn_attrs.rs | 3 +- compiler/rustc_middle/src/middle/privacy.rs | 9 +- compiler/rustc_middle/src/mir/consts.rs | 10 +- compiler/rustc_middle/src/mir/coverage.rs | 8 - .../rustc_middle/src/mir/interpret/error.rs | 2 +- .../rustc_middle/src/mir/interpret/value.rs | 2 +- compiler/rustc_middle/src/mir/mod.rs | 31 +- compiler/rustc_middle/src/mir/query.rs | 65 +- compiler/rustc_middle/src/mir/statement.rs | 4 +- compiler/rustc_middle/src/mir/syntax.rs | 24 +- compiler/rustc_middle/src/mir/tcx.rs | 10 +- compiler/rustc_middle/src/mir/terminator.rs | 73 +- compiler/rustc_middle/src/mir/visit.rs | 2 +- compiler/rustc_middle/src/query/mod.rs | 6 - .../rustc_middle/src/query/on_disk_cache.rs | 7 +- compiler/rustc_middle/src/query/plumbing.rs | 4 +- compiler/rustc_middle/src/thir.rs | 45 +- compiler/rustc_middle/src/thir/visit.rs | 10 +- compiler/rustc_middle/src/traits/mod.rs | 2 +- compiler/rustc_middle/src/traits/select.rs | 52 +- .../rustc_middle/src/traits/solve/inspect.rs | 7 +- .../src/traits/solve/inspect/format.rs | 8 +- compiler/rustc_middle/src/ty/binding.rs | 18 - compiler/rustc_middle/src/ty/cast.rs | 4 +- compiler/rustc_middle/src/ty/codec.rs | 1 - compiler/rustc_middle/src/ty/consts.rs | 2 +- compiler/rustc_middle/src/ty/consts/kind.rs | 4 +- compiler/rustc_middle/src/ty/context.rs | 134 +- compiler/rustc_middle/src/ty/instance.rs | 66 +- compiler/rustc_middle/src/ty/mod.rs | 27 +- .../src/ty/normalize_erasing_regions.rs | 4 +- compiler/rustc_middle/src/ty/print/pretty.rs | 2 +- compiler/rustc_middle/src/ty/region.rs | 4 + .../rustc_middle/src/ty/structural_impls.rs | 2 + compiler/rustc_middle/src/ty/sty.rs | 29 +- .../rustc_middle/src/ty/typeck_results.rs | 85 +- compiler/rustc_mir_build/src/build/block.rs | 49 +- .../rustc_mir_build/src/build/custom/mod.rs | 5 +- .../rustc_mir_build/src/build/custom/parse.rs | 2 +- .../src/build/expr/as_rvalue.rs | 17 +- .../rustc_mir_build/src/build/expr/into.rs | 2 +- .../rustc_mir_build/src/build/matches/mod.rs | 260 +- .../src/build/matches/simplify.rs | 25 +- .../rustc_mir_build/src/build/matches/test.rs | 14 +- .../rustc_mir_build/src/build/matches/util.rs | 18 +- compiler/rustc_mir_build/src/build/mod.rs | 66 +- compiler/rustc_mir_build/src/build/scope.rs | 20 +- .../rustc_mir_build/src/check_unsafety.rs | 19 +- compiler/rustc_mir_build/src/errors.rs | 23 +- compiler/rustc_mir_build/src/thir/cx/expr.rs | 3 +- .../src/thir/pattern/check_match.rs | 84 +- .../rustc_mir_build/src/thir/pattern/mod.rs | 31 +- compiler/rustc_mir_build/src/thir/print.rs | 3 +- .../rustc_mir_dataflow/src/elaborate_drops.rs | 10 +- compiler/rustc_mir_transform/messages.ftl | 49 - .../rustc_mir_transform/src/check_unsafety.rs | 615 --- compiler/rustc_mir_transform/src/coroutine.rs | 12 +- .../src/coroutine/by_move_body.rs | 171 +- .../rustc_mir_transform/src/coverage/mod.rs | 7 +- .../rustc_mir_transform/src/coverage/query.rs | 2 +- .../src/elaborate_box_derefs.rs | 12 +- compiler/rustc_mir_transform/src/errors.rs | 171 +- compiler/rustc_mir_transform/src/gvn.rs | 2 +- compiler/rustc_mir_transform/src/inline.rs | 4 +- .../rustc_mir_transform/src/inline/cycle.rs | 20 +- .../rustc_mir_transform/src/instsimplify.rs | 15 + .../src/known_panics_lint.rs | 12 +- compiler/rustc_mir_transform/src/lib.rs | 14 +- .../src/lower_intrinsics.rs | 2 + compiler/rustc_mir_transform/src/nrvo.rs | 7 +- .../rustc_mir_transform/src/promote_consts.rs | 3 +- compiler/rustc_mir_transform/src/shim.rs | 4 +- compiler/rustc_mir_transform/src/simplify.rs | 8 +- ...ching.rs => unreachable_enum_branching.rs} | 57 +- compiler/rustc_monomorphize/src/collector.rs | 21 +- compiler/rustc_monomorphize/src/lib.rs | 16 +- .../src/canonicalizer.rs | 89 +- compiler/rustc_parse/messages.ftl | 3 - compiler/rustc_parse/src/errors.rs | 8 - compiler/rustc_parse/src/lexer/mod.rs | 2 +- .../rustc_parse/src/parser/attr_wrapper.rs | 2 +- compiler/rustc_parse/src/parser/expr.rs | 1 + compiler/rustc_parse/src/parser/mod.rs | 18 +- compiler/rustc_parse/src/parser/pat.rs | 43 +- compiler/rustc_parse_format/src/lib.rs | 2 +- compiler/rustc_passes/src/entry.rs | 13 +- compiler/rustc_pattern_analysis/Cargo.toml | 2 +- .../rustc_pattern_analysis/src/constructor.rs | 40 +- compiler/rustc_pattern_analysis/src/lib.rs | 49 +- compiler/rustc_pattern_analysis/src/rustc.rs | 2 - .../rustc_pattern_analysis/src/usefulness.rs | 49 +- .../rustc_query_system/src/dep_graph/graph.rs | 24 +- .../src/dep_graph/serialized.rs | 200 +- compiler/rustc_resolve/src/ident.rs | 2 + compiler/rustc_resolve/src/imports.rs | 6 +- compiler/rustc_session/messages.ftl | 2 + compiler/rustc_session/src/config.rs | 39 +- compiler/rustc_session/src/errors.rs | 4 + compiler/rustc_session/src/options.rs | 9 +- compiler/rustc_session/src/session.rs | 80 +- compiler/rustc_session/src/utils.rs | 2 +- compiler/rustc_smir/src/rustc_smir/context.rs | 5 +- .../rustc_smir/src/rustc_smir/convert/mir.rs | 5 +- compiler/rustc_span/src/def_id.rs | 2 +- compiler/rustc_span/src/hygiene.rs | 4 +- compiler/rustc_span/src/lib.rs | 12 + compiler/rustc_span/src/source_map.rs | 15 + compiler/rustc_span/src/source_map/tests.rs | 2 +- compiler/rustc_span/src/symbol.rs | 33 +- compiler/rustc_symbol_mangling/src/legacy.rs | 12 +- compiler/rustc_symbol_mangling/src/typeid.rs | 20 +- .../src/typeid/typeid_itanium_cxx_abi.rs | 593 +-- compiler/rustc_symbol_mangling/src/v0.rs | 8 +- compiler/rustc_target/src/abi/call/mod.rs | 27 +- compiler/rustc_trait_selection/src/infer.rs | 3 +- compiler/rustc_trait_selection/src/lib.rs | 2 +- .../src/solve/alias_relate.rs | 159 +- .../src/solve/assembly/mod.rs | 13 +- .../src/solve/eval_ctxt/canonical.rs | 2 +- .../src/solve/eval_ctxt/commit_if_ok.rs | 47 - .../src/solve/eval_ctxt/mod.rs | 91 +- .../src/solve/eval_ctxt/select.rs | 12 +- .../src/solve/inspect/analyse.rs | 10 +- .../src/solve/inspect/build.rs | 27 - .../src/solve/normalize.rs | 4 +- .../src/solve/normalizes_to/mod.rs | 69 +- .../src/solve/normalizes_to/opaque_types.rs | 6 - .../src/traits/auto_trait.rs | 62 +- .../src/traits/coherence.rs | 18 +- .../src/traits/error_reporting/suggestions.rs | 6 +- .../error_reporting/type_err_ctxt_ext.rs | 9 +- .../src/traits/fulfill.rs | 19 +- .../rustc_trait_selection/src/traits/mod.rs | 8 +- .../src/traits/normalize.rs | 2 + .../src/traits/project.rs | 9 +- .../src/traits/query/type_op/normalize.rs | 2 +- .../src/traits/select/confirmation.rs | 120 +- .../src/traits/select/mod.rs | 155 +- .../src/traits/specialize/mod.rs | 20 +- .../traits/specialize/specialization_graph.rs | 2 +- .../rustc_trait_selection/src/traits/util.rs | 44 +- .../rustc_trait_selection/src/traits/wf.rs | 26 +- compiler/rustc_ty_utils/src/consts.rs | 2 +- compiler/rustc_ty_utils/src/layout.rs | 44 +- compiler/rustc_ty_utils/src/opaque_types.rs | 3 +- compiler/rustc_ty_utils/src/sig_types.rs | 11 +- compiler/rustc_type_ir/src/flags.rs | 6 +- compiler/rustc_type_ir/src/fold.rs | 51 +- compiler/rustc_type_ir/src/interner.rs | 13 +- compiler/rustc_type_ir/src/lib.rs | 2 +- compiler/rustc_type_ir/src/new.rs | 2 + compiler/rustc_type_ir/src/visit.rs | 4 +- compiler/stable_mir/src/mir/body.rs | 7 +- compiler/stable_mir/src/ty.rs | 4 +- library/alloc/benches/vec_deque_append.rs | 5 + .../alloc/src/collections/vec_deque/mod.rs | 8 +- library/alloc/src/lib.miri.rs | 4 + library/alloc/src/lib.rs | 3 +- library/alloc/src/raw_vec.rs | 8 +- library/alloc/src/string.rs | 6 +- library/alloc/src/vec/mod.rs | 4 +- library/alloc/tests/lib.rs | 2 +- library/core/src/any.rs | 8 +- library/core/src/char/mod.rs | 309 +- library/core/src/clone.rs | 3 + library/core/src/cmp.rs | 43 +- library/core/src/default.rs | 4 + library/core/src/fmt/mod.rs | 4 +- library/core/src/hash/mod.rs | 14 +- library/core/src/intrinsics.rs | 45 +- library/core/src/intrinsics/simd.rs | 13 + library/core/src/lib.miri.rs | 4 + library/core/src/lib.rs | 6 +- library/core/src/macros/mod.rs | 3 +- library/core/src/marker.rs | 13 + library/core/src/num/error.rs | 3 +- library/core/src/num/int_macros.rs | 106 +- library/core/src/num/mod.rs | 304 +- library/core/src/num/nonzero.rs | 3 +- library/core/src/num/uint_macros.rs | 107 +- library/core/src/panicking.rs | 65 +- library/core/src/ptr/const_ptr.rs | 40 +- library/core/src/ptr/metadata.rs | 4 +- library/core/src/ptr/mod.rs | 48 +- library/core/src/ptr/mut_ptr.rs | 41 +- library/core/src/ptr/non_null.rs | 20 +- library/core/src/slice/index.rs | 11 + library/core/src/slice/mod.rs | 8 +- library/core/src/sync/atomic.rs | 12 +- library/core/src/task/wake.rs | 67 +- library/core/tests/intrinsics.rs | 27 + library/core/tests/lib.rs | 5 +- library/core/tests/num/mod.rs | 10 + library/core/tests/ptr.rs | 11 +- .../core_simd/src/simd/ptr/const_ptr.rs | 18 +- .../crates/core_simd/src/simd/ptr/mut_ptr.rs | 18 +- .../crates/core_simd/tests/pointers.rs | 18 +- library/proc_macro/src/lib.rs | 4 +- library/std/src/fs.rs | 4 +- library/std/src/lib.miri.rs | 4 + library/std/src/lib.rs | 3 +- library/std/src/os/xous/ffi.rs | 2 +- library/std/src/panic.rs | 3 + library/std/src/panicking.rs | 3 +- library/std/src/rt.rs | 11 +- library/std/src/sys/pal/hermit/thread.rs | 14 +- library/std/src/sys/pal/itron/thread.rs | 14 +- library/std/src/sys/pal/mod.rs | 6 +- library/std/src/sys/pal/sgx/thread.rs | 10 - library/std/src/sys/pal/teeos/thread.rs | 12 - library/std/src/sys/pal/uefi/thread.rs | 10 - library/std/src/sys/pal/unix/rand.rs | 20 +- .../std/src/sys/pal/unix/stack_overflow.rs | 312 +- library/std/src/sys/pal/unix/thread.rs | 341 +- library/std/src/sys/pal/unsupported/thread.rs | 10 - library/std/src/sys/pal/wasi/thread.rs | 10 - .../std/src/sys/pal/wasip2/cabi_realloc.rs | 65 + library/std/src/sys/pal/wasip2/mod.rs | 8 +- .../std/src/sys/pal/windows/stack_overflow.rs | 30 +- .../src/sys/pal/windows/stack_overflow_uwp.rs | 9 +- library/std/src/sys/pal/windows/thread.rs | 16 +- library/std/src/sys/pal/xous/thread.rs | 10 - .../std/src/sys/pal/xous/thread_local_key.rs | 2 +- .../std/src/sys/pal/zkvm/thread_local_key.rs | 4 +- library/std/src/sys/personality/dwarf/eh.rs | 2 +- library/std/src/sys/sync/rwlock/queue.rs | 6 +- library/std/src/sys_common/mod.rs | 1 - library/std/src/sys_common/thread_info.rs | 53 - library/std/src/thread/mod.rs | 32 +- src/bootstrap/bootstrap.py | 1 + src/bootstrap/src/bin/rustc.rs | 14 +- src/bootstrap/src/core/build_steps/compile.rs | 20 +- src/bootstrap/src/core/build_steps/dist.rs | 22 +- src/bootstrap/src/core/build_steps/llvm.rs | 1 + src/bootstrap/src/core/build_steps/run.rs | 34 +- src/bootstrap/src/core/build_steps/test.rs | 231 +- src/bootstrap/src/core/build_steps/tool.rs | 12 +- src/bootstrap/src/core/builder.rs | 174 +- src/bootstrap/src/core/builder/tests.rs | 2 - src/bootstrap/src/core/config/config.rs | 7 +- src/bootstrap/src/core/config/flags.rs | 31 +- src/bootstrap/src/lib.rs | 2 + src/bootstrap/src/utils/render_tests.rs | 12 +- .../docker/host-x86_64/mingw-check/Dockerfile | 2 +- .../x86_64-gnu-tools/browser-ui-test.version | 2 +- .../x86_64-gnu-tools/checktools.sh | 6 + src/ci/docker/run.sh | 1 + src/ci/scripts/upload-artifacts.sh | 14 + src/doc/rustc/src/codegen-options/index.md | 14 +- src/doc/rustc/src/platform-support.md | 1 + src/doc/rustc/src/platform-support/netbsd.md | 8 +- src/doc/rustdoc/src/what-is-rustdoc.md | 11 +- .../src/compiler-flags/external-clangrt.md | 6 + .../src/compiler-flags/remap-path-scope.md | 8 +- .../src/compiler-flags/sanitizer.md | 15 + src/etc/completions/x.py.fish | 42 +- src/etc/completions/x.py.ps1 | 49 +- src/etc/completions/x.py.sh | 133 +- src/etc/completions/x.py.zsh | 56 +- src/etc/lldb_commands | 3 + src/etc/lldb_lookup.py | 5 + src/etc/lldb_providers.py | 29 + src/etc/rust_types.py | 6 + src/librustdoc/Cargo.toml | 4 +- src/librustdoc/clean/auto_trait.rs | 1030 ++--- src/librustdoc/clean/blanket_impl.rs | 2 +- src/librustdoc/clean/mod.rs | 109 +- src/librustdoc/clean/simplify.rs | 45 +- src/librustdoc/clean/types.rs | 7 - src/librustdoc/clean/utils.rs | 16 +- src/librustdoc/html/render/mod.rs | 35 +- src/librustdoc/html/render/search_index.rs | 107 +- .../html/render/search_index/encode.rs | 243 ++ src/librustdoc/html/render/write_shared.rs | 31 +- src/librustdoc/html/static/.eslintrc.js | 2 +- src/librustdoc/html/static/css/noscript.css | 4 +- src/librustdoc/html/static/css/rustdoc.css | 8 +- src/librustdoc/html/static/js/main.js | 32 +- src/librustdoc/html/static/js/search.js | 509 ++- src/librustdoc/html/static/js/storage.js | 4 +- src/tools/cargo | 2 +- src/tools/clippy/.gitignore | 3 + src/tools/clippy/CHANGELOG.md | 2 + src/tools/clippy/Cargo.toml | 1 - src/tools/clippy/book/src/SUMMARY.md | 1 + .../book/src/development/defining_lints.md | 9 +- .../book/src/development/lint_passes.md | 2 +- .../clippy/book/src/development/the_team.md | 130 + .../clippy/book/src/lint_configuration.md | 1 + src/tools/clippy/clippy_config/src/conf.rs | 7 +- src/tools/clippy/clippy_config/src/lib.rs | 8 +- src/tools/clippy/clippy_config/src/msrvs.rs | 2 +- src/tools/clippy/clippy_dev/src/lib.rs | 9 +- src/tools/clippy/clippy_dev/src/lint.rs | 4 + .../clippy/clippy_dev/src/update_lints.rs | 4 +- .../clippy/clippy_lints/src/approx_const.rs | 2 +- .../clippy/clippy_lints/src/asm_syntax.rs | 4 +- .../src/assertions_on_constants.rs | 6 +- .../clippy_lints/src/assigning_clones.rs | 30 +- .../attrs/allow_attributes_without_reason.rs | 2 +- .../src/attrs/duplicated_attributes.rs | 11 + .../clippy_lints/src/attrs/inline_always.rs | 2 +- .../src/attrs/maybe_misused_cfg.rs | 2 +- .../src/attrs/mixed_attributes_style.rs | 85 +- .../clippy/clippy_lints/src/attrs/mod.rs | 20 +- .../src/attrs/unnecessary_clippy_cfg.rs | 2 +- .../clippy_lints/src/await_holding_invalid.rs | 2 +- .../clippy_lints/src/blocks_in_conditions.rs | 4 +- .../src/bool_assert_comparison.rs | 2 +- src/tools/clippy/clippy_lints/src/booleans.rs | 4 +- .../clippy/clippy_lints/src/box_default.rs | 55 +- .../clippy_lints/src/cargo/common_metadata.rs | 2 +- .../clippy_lints/src/cargo/feature_name.rs | 4 +- .../src/cargo/lint_groups_priority.rs | 2 +- .../clippy/clippy_lints/src/cargo/mod.rs | 4 +- .../src/cargo/multiple_crate_versions.rs | 2 +- .../src/cargo/wildcard_dependencies.rs | 2 +- .../clippy_lints/src/casts/as_ptr_cast_mut.rs | 2 +- .../src/casts/cast_abs_to_unsigned.rs | 2 +- .../clippy_lints/src/casts/cast_lossless.rs | 2 +- .../clippy_lints/src/casts/cast_nan_to_int.rs | 2 +- .../src/casts/cast_possible_truncation.rs | 8 +- .../src/casts/cast_possible_wrap.rs | 2 +- .../src/casts/cast_precision_loss.rs | 2 +- .../src/casts/cast_ptr_alignment.rs | 2 +- .../clippy_lints/src/casts/cast_sign_loss.rs | 7 +- .../src/casts/cast_slice_different_sizes.rs | 2 +- .../src/casts/cast_slice_from_raw_parts.rs | 2 +- .../src/casts/fn_to_numeric_cast.rs | 2 +- .../src/casts/fn_to_numeric_cast_any.rs | 2 +- .../fn_to_numeric_cast_with_truncation.rs | 2 +- .../clippy_lints/src/casts/ptr_as_ptr.rs | 2 +- .../src/casts/ptr_cast_constness.rs | 2 +- .../src/casts/unnecessary_cast.rs | 6 +- .../clippy_lints/src/cognitive_complexity.rs | 2 +- .../src/collection_is_never_read.rs | 2 +- .../clippy/clippy_lints/src/declared_lints.rs | 2 + src/tools/clippy/clippy_lints/src/default.rs | 4 +- .../src/default_constructed_unit_structs.rs | 2 +- .../src/default_instead_of_iter_empty.rs | 4 +- .../src/default_union_representation.rs | 2 +- .../clippy_lints/src/derivable_impls.rs | 2 +- src/tools/clippy/clippy_lints/src/derive.rs | 3 +- .../clippy_lints/src/disallowed_macros.rs | 4 +- .../clippy_lints/src/disallowed_methods.rs | 2 +- .../clippy_lints/src/disallowed_names.rs | 2 +- .../src/disallowed_script_idents.rs | 2 +- .../clippy_lints/src/disallowed_types.rs | 2 +- .../clippy_lints/src/drop_forget_ref.rs | 4 +- .../clippy/clippy_lints/src/duplicate_mod.rs | 2 +- .../clippy/clippy_lints/src/endian_bytes.rs | 2 +- src/tools/clippy/clippy_lints/src/entry.rs | 2 +- src/tools/clippy/clippy_lints/src/escape.rs | 2 +- .../clippy/clippy_lints/src/eta_reduction.rs | 8 +- .../clippy_lints/src/excessive_bools.rs | 4 +- .../clippy_lints/src/exhaustive_items.rs | 2 +- .../clippy/clippy_lints/src/explicit_write.rs | 2 +- .../src/extra_unused_type_parameters.rs | 8 +- .../clippy/clippy_lints/src/float_literal.rs | 6 +- .../src/floating_point_arithmetic.rs | 6 +- .../clippy/clippy_lints/src/format_args.rs | 6 +- .../clippy/clippy_lints/src/format_impl.rs | 4 +- .../clippy/clippy_lints/src/formatting.rs | 17 +- .../src/from_raw_with_void_ptr.rs | 4 +- .../src/functions/misnamed_getters.rs | 4 +- .../clippy_lints/src/functions/must_use.rs | 6 +- .../clippy_lints/src/functions/result.rs | 4 +- .../src/functions/too_many_arguments.rs | 2 +- .../src/functions/too_many_lines.rs | 2 +- .../src/if_then_some_else_none.rs | 4 +- .../clippy_lints/src/implicit_hasher.rs | 4 +- .../src/implied_bounds_in_impls.rs | 4 +- .../clippy_lints/src/incompatible_msrv.rs | 4 +- .../clippy_lints/src/index_refutable_slice.rs | 4 +- .../clippy_lints/src/inherent_to_string.rs | 8 +- .../src/inline_fn_without_body.rs | 2 +- .../clippy_lints/src/instant_subtraction.rs | 4 +- .../src/integer_division_remainder_used.rs | 2 +- .../src/invalid_upcast_comparisons.rs | 4 +- .../clippy_lints/src/item_name_repetitions.rs | 8 +- .../src/iter_not_returning_iterator.rs | 2 +- .../src/iter_without_into_iter.rs | 8 +- .../clippy/clippy_lints/src/large_futures.rs | 2 +- .../clippy_lints/src/large_include_file.rs | 2 +- .../clippy_lints/src/large_stack_arrays.rs | 4 +- .../clippy_lints/src/large_stack_frames.rs | 106 +- .../src/legacy_numeric_constants.rs | 293 ++ src/tools/clippy/clippy_lints/src/len_zero.rs | 46 +- src/tools/clippy/clippy_lints/src/lib.rs | 15 +- .../clippy/clippy_lints/src/lifetimes.rs | 2 +- .../clippy_lints/src/lines_filter_map_ok.rs | 2 +- .../src/literal_representation.rs | 8 +- .../src/loops/explicit_counter_loop.rs | 4 +- .../clippy_lints/src/loops/for_kv_map.rs | 2 +- .../clippy_lints/src/loops/manual_flatten.rs | 2 +- .../clippy_lints/src/loops/mut_range_bound.rs | 4 +- .../src/loops/needless_range_loop.rs | 6 +- .../clippy_lints/src/loops/never_loop.rs | 9 +- .../clippy_lints/src/loops/same_item_push.rs | 2 +- .../src/loops/single_element_loop.rs | 6 +- .../clippy/clippy_lints/src/main_recursion.rs | 2 +- .../clippy/clippy_lints/src/manual_assert.rs | 5 +- .../clippy/clippy_lints/src/manual_bits.rs | 2 +- .../clippy/clippy_lints/src/manual_clamp.rs | 38 +- .../clippy/clippy_lints/src/manual_strip.rs | 4 +- .../src/manual_unwrap_or_default.rs | 33 +- .../clippy/clippy_lints/src/map_unit_fn.rs | 4 +- .../clippy_lints/src/match_result_ok.rs | 2 +- .../src/matches/collapsible_match.rs | 2 +- .../matches/infallible_destructuring_match.rs | 4 +- .../src/matches/manual_unwrap_or.rs | 2 +- .../clippy_lints/src/matches/match_as_ref.rs | 4 +- .../src/matches/match_like_matches.rs | 2 +- .../src/matches/match_str_case_mismatch.rs | 2 +- .../src/matches/match_wild_err_arm.rs | 2 +- .../clippy/clippy_lints/src/matches/mod.rs | 32 +- .../src/matches/needless_match.rs | 2 +- .../src/matches/redundant_guards.rs | 2 +- .../src/matches/redundant_pattern_match.rs | 8 +- .../clippy/clippy_lints/src/mem_replace.rs | 2 +- .../src/methods/bind_instead_of_map.rs | 6 +- .../clippy_lints/src/methods/bytes_nth.rs | 4 +- .../clippy_lints/src/methods/chars_cmp.rs | 2 +- .../src/methods/chars_cmp_with_unwrap.rs | 2 +- .../src/methods/clear_with_drain.rs | 5 +- .../clippy_lints/src/methods/clone_on_copy.rs | 4 +- .../clippy_lints/src/methods/drain_collect.rs | 2 +- .../src/methods/expect_fun_call.rs | 4 +- .../src/methods/filetype_is_file.rs | 2 +- .../clippy_lints/src/methods/filter_map.rs | 53 +- .../src/methods/filter_map_identity.rs | 18 +- .../clippy_lints/src/methods/get_first.rs | 4 +- .../src/methods/get_last_with_len.rs | 2 +- .../clippy_lints/src/methods/get_unwrap.rs | 2 +- .../src/methods/implicit_clone.rs | 2 +- .../src/methods/inefficient_to_string.rs | 2 +- .../src/methods/into_iter_on_ref.rs | 2 +- .../src/methods/is_digit_ascii_radix.rs | 2 +- .../clippy_lints/src/methods/is_empty.rs | 2 +- .../src/methods/iter_cloned_collect.rs | 2 +- .../clippy_lints/src/methods/iter_count.rs | 2 +- .../clippy_lints/src/methods/iter_filter.rs | 14 +- .../clippy_lints/src/methods/iter_kv_map.rs | 9 +- .../clippy_lints/src/methods/iter_nth.rs | 2 +- .../iter_on_single_or_empty_collections.rs | 4 +- .../src/methods/iter_overeager_cloned.rs | 2 +- .../src/methods/iter_with_drain.rs | 2 +- .../methods/manual_saturating_arithmetic.rs | 2 +- .../clippy_lints/src/methods/map_clone.rs | 9 +- .../clippy_lints/src/methods/map_flatten.rs | 4 +- .../clippy_lints/src/methods/map_identity.rs | 2 +- .../clippy/clippy_lints/src/methods/mod.rs | 51 +- .../src/methods/needless_collect.rs | 2 +- .../clippy_lints/src/methods/open_options.rs | 2 +- .../src/methods/option_as_ref_cloned.rs | 2 +- .../src/methods/option_as_ref_deref.rs | 4 +- .../src/methods/option_map_unwrap_or.rs | 2 +- .../clippy_lints/src/methods/or_fun_call.rs | 4 +- .../src/methods/range_zip_with_len.rs | 2 +- .../src/methods/search_is_some.rs | 12 +- .../src/methods/stable_sort_primitive.rs | 2 +- .../clippy_lints/src/methods/str_splitn.rs | 4 +- .../src/methods/suspicious_splitn.rs | 2 +- .../src/methods/suspicious_to_owned.rs | 2 +- .../src/methods/type_id_on_box.rs | 62 +- .../src/methods/unnecessary_filter_map.rs | 2 +- .../src/methods/unnecessary_get_then_check.rs | 4 +- .../src/methods/unnecessary_iter_cloned.rs | 2 +- .../src/methods/unnecessary_literal_unwrap.rs | 2 +- .../src/methods/unnecessary_to_owned.rs | 19 +- .../src/methods/unwrap_expect_used.rs | 2 +- .../clippy_lints/src/methods/useless_asref.rs | 8 +- .../clippy/clippy_lints/src/methods/utils.rs | 11 +- .../src/methods/verbose_file_reads.rs | 2 +- .../src/methods/wrong_self_convention.rs | 2 +- .../clippy_lints/src/min_ident_chars.rs | 2 +- src/tools/clippy/clippy_lints/src/misc.rs | 6 +- .../src/misc_early/builtin_type_shadow.rs | 2 +- .../src/misc_early/literal_suffix.rs | 4 +- .../clippy/clippy_lints/src/misc_early/mod.rs | 2 +- .../src/misc_early/redundant_pattern.rs | 2 +- .../src/misc_early/unneeded_field_pattern.rs | 4 +- .../src/mismatching_type_param_order.rs | 2 +- .../src/missing_asserts_for_indexing.rs | 2 +- .../clippy/clippy_lints/src/missing_doc.rs | 2 +- .../src/missing_fields_in_debug.rs | 2 +- .../clippy/clippy_lints/src/missing_inline.rs | 2 +- .../clippy_lints/src/missing_trait_methods.rs | 2 +- .../src/mixed_read_write_in_expression.rs | 2 +- .../clippy/clippy_lints/src/module_style.rs | 8 +- .../src/multiple_unsafe_ops_per_block.rs | 2 +- .../clippy/clippy_lints/src/mut_reference.rs | 7 +- .../src/mutable_debug_assertion.rs | 2 +- .../clippy/clippy_lints/src/mutex_atomic.rs | 6 +- .../clippy/clippy_lints/src/needless_bool.rs | 28 +- .../clippy_lints/src/needless_borrowed_ref.rs | 2 +- .../src/needless_borrows_for_generic_args.rs | 6 +- .../clippy_lints/src/needless_continue.rs | 2 +- .../src/needless_question_mark.rs | 2 +- .../clippy_lints/src/new_without_default.rs | 4 +- .../clippy/clippy_lints/src/no_effect.rs | 2 +- .../clippy/clippy_lints/src/non_copy_const.rs | 4 +- .../clippy_lints/src/non_expressive_names.rs | 2 +- .../src/non_send_fields_in_send_ty.rs | 2 +- .../src/nonstandard_macro_braces.rs | 2 +- .../clippy/clippy_lints/src/octal_escapes.rs | 2 +- .../operators/absurd_extreme_comparisons.rs | 2 +- .../clippy_lints/src/operators/bit_mask.rs | 16 +- .../src/operators/const_comparisons.rs | 6 +- .../src/operators/duration_subsec.rs | 2 +- .../clippy_lints/src/operators/eq_op.rs | 4 +- .../src/operators/modulo_arithmetic.rs | 2 +- .../clippy_lints/src/operators/ptr_eq.rs | 2 +- .../src/operators/self_assignment.rs | 2 +- .../clippy_lints/src/option_if_let_else.rs | 2 +- .../clippy_lints/src/pass_by_ref_or_value.rs | 4 +- .../clippy_lints/src/pattern_type_mismatch.rs | 6 +- src/tools/clippy/clippy_lints/src/ptr.rs | 6 +- .../clippy_lints/src/ptr_offset_with_cast.rs | 4 +- .../clippy/clippy_lints/src/question_mark.rs | 44 +- src/tools/clippy/clippy_lints/src/ranges.rs | 4 +- .../src/redundant_closure_call.rs | 34 +- .../clippy_lints/src/redundant_locals.rs | 4 +- .../clippy_lints/src/redundant_pub_crate.rs | 2 +- .../src/redundant_static_lifetimes.rs | 2 +- .../src/redundant_type_annotations.rs | 2 +- src/tools/clippy/clippy_lints/src/regex.rs | 6 +- .../src/repeat_vec_with_capacity.rs | 2 +- src/tools/clippy/clippy_lints/src/returns.rs | 3 +- .../src/self_named_constructors.rs | 2 +- .../clippy/clippy_lints/src/serde_api.rs | 2 +- src/tools/clippy/clippy_lints/src/shadow.rs | 2 +- .../src/single_range_in_vec_init.rs | 2 +- .../src/slow_vector_initialization.rs | 2 +- .../clippy_lints/src/std_instead_of_core.rs | 4 +- src/tools/clippy/clippy_lints/src/strings.rs | 4 +- .../clippy_lints/src/suspicious_trait_impl.rs | 2 +- src/tools/clippy/clippy_lints/src/swap.rs | 182 +- ...ead_local_initializer_can_be_made_const.rs | 4 +- .../clippy_lints/src/trailing_empty_array.rs | 2 +- .../clippy/clippy_lints/src/trait_bounds.rs | 8 +- .../src/transmute/crosspointer_transmute.rs | 4 +- .../missing_transmute_annotations.rs | 87 + .../clippy/clippy_lints/src/transmute/mod.rs | 34 + .../src/transmute/transmute_float_to_int.rs | 2 +- .../src/transmute/transmute_int_to_bool.rs | 2 +- .../src/transmute/transmute_int_to_char.rs | 2 +- .../src/transmute/transmute_int_to_float.rs | 2 +- .../transmute/transmute_int_to_non_zero.rs | 2 +- .../src/transmute/transmute_num_to_bytes.rs | 2 +- .../src/transmute/transmute_ptr_to_ref.rs | 2 +- .../src/transmute/transmute_ref_to_ref.rs | 2 +- .../src/transmute/transmute_undefined_repr.rs | 10 +- .../transmutes_expressible_as_ptr_casts.rs | 2 +- .../transmute/unsound_collection_transmute.rs | 2 +- .../src/transmute/useless_transmute.rs | 2 +- .../src/transmute/wrong_transmute.rs | 2 +- .../clippy_lints/src/types/box_collection.rs | 4 +- .../src/types/redundant_allocation.rs | 6 +- .../src/unconditional_recursion.rs | 2 +- .../src/undocumented_unsafe_blocks.rs | 22 +- .../src/unit_return_expecting_ord.rs | 4 +- .../src/unit_types/let_unit_value.rs | 44 +- .../clippy_lints/src/unit_types/unit_arg.rs | 6 +- .../clippy_lints/src/unit_types/unit_cmp.rs | 4 +- .../src/unnecessary_box_returns.rs | 2 +- .../src/unnecessary_map_on_constructor.rs | 4 +- .../clippy_lints/src/unnecessary_wraps.rs | 2 +- .../clippy_lints/src/unnested_or_patterns.rs | 6 +- .../src/unsafe_removed_from_name.rs | 2 +- .../clippy_lints/src/unused_io_amount.rs | 24 +- .../clippy_lints/src/unused_rounding.rs | 4 +- src/tools/clippy/clippy_lints/src/unwrap.rs | 4 +- .../clippy_lints/src/unwrap_in_result.rs | 2 +- .../clippy_lints/src/upper_case_acronyms.rs | 2 +- src/tools/clippy/clippy_lints/src/use_self.rs | 4 +- .../clippy_lints/src/useless_conversion.rs | 14 +- .../clippy/clippy_lints/src/utils/author.rs | 6 +- .../almost_standard_lint_formulation.rs | 2 +- .../utils/internal_lints/collapsible_calls.rs | 3 +- .../internal_lints/compiler_lint_functions.rs | 2 +- .../internal_lints/lint_without_lint_pass.rs | 6 +- .../internal_lints/metadata_collector.rs | 16 +- .../utils/internal_lints/msrv_attr_impl.rs | 4 +- .../internal_lints/unnecessary_def_path.rs | 13 +- .../clippy/clippy_lints/src/visibility.rs | 2 +- src/tools/clippy/clippy_lints/src/write.rs | 8 +- .../clippy/clippy_lints/src/zero_div_zero.rs | 2 +- .../clippy/clippy_utils/src/ast_utils.rs | 20 +- src/tools/clippy/clippy_utils/src/attrs.rs | 32 +- .../clippy_utils/src/check_proc_macro.rs | 12 +- src/tools/clippy/clippy_utils/src/consts.rs | 10 +- .../clippy/clippy_utils/src/diagnostics.rs | 55 +- src/tools/clippy/clippy_utils/src/higher.rs | 54 +- .../clippy/clippy_utils/src/hir_utils.rs | 3 +- src/tools/clippy/clippy_utils/src/lib.rs | 86 +- src/tools/clippy/clippy_utils/src/macros.rs | 2 +- src/tools/clippy/clippy_utils/src/mir/mod.rs | 2 +- .../clippy_utils/src/qualify_min_const_fn.rs | 4 +- src/tools/clippy/clippy_utils/src/sugg.rs | 79 +- src/tools/clippy/clippy_utils/src/ty.rs | 42 +- src/tools/clippy/clippy_utils/src/usage.rs | 18 +- src/tools/clippy/clippy_utils/src/visitors.rs | 28 +- src/tools/clippy/lintcheck/src/main.rs | 41 +- src/tools/clippy/rust-toolchain | 2 +- src/tools/clippy/tests/compile-test.rs | 2 + .../ui-toml/absolute_paths/absolute_paths.rs | 2 +- .../large_stack_frames/large_stack_frames.rs | 2 +- .../large_stack_frames.stderr | 17 +- .../clippy/tests/ui/assigning_clones.fixed | 13 + src/tools/clippy/tests/ui/assigning_clones.rs | 13 + .../clippy/tests/ui/assigning_clones.stderr | 12 +- .../clippy/tests/ui/author/issue_3849.rs | 2 +- .../clippy/tests/ui/auxiliary/macro_rules.rs | 7 + .../tests/ui/blocks_in_conditions.fixed | 7 +- .../clippy/tests/ui/blocks_in_conditions.rs | 7 +- .../tests/ui/blocks_in_conditions.stderr | 8 +- src/tools/clippy/tests/ui/box_default.fixed | 85 +- src/tools/clippy/tests/ui/box_default.rs | 75 +- src/tools/clippy/tests/ui/box_default.stderr | 104 +- src/tools/clippy/tests/ui/cast.rs | 12 +- src/tools/clippy/tests/ui/cast.stderr | 190 +- .../clippy/tests/ui/checked_conversions.fixed | 1 + .../clippy/tests/ui/checked_conversions.rs | 1 + .../tests/ui/checked_conversions.stderr | 34 +- .../clippy/tests/ui/crashes/ice-12616.fixed | 7 + .../clippy/tests/ui/crashes/ice-12616.rs | 7 + .../clippy/tests/ui/crashes/ice-12616.stderr | 19 + src/tools/clippy/tests/ui/crashes/ice-1782.rs | 2 +- .../clippy/tests/ui/duplicated_attributes.rs | 13 +- .../tests/ui/duplicated_attributes.stderr | 94 +- .../clippy/tests/ui/eager_transmute.fixed | 2 +- src/tools/clippy/tests/ui/eager_transmute.rs | 2 +- .../clippy/tests/ui/filter_map_identity.fixed | 86 +- .../clippy/tests/ui/filter_map_identity.rs | 86 +- .../tests/ui/filter_map_identity.stderr | 134 +- .../clippy/tests/ui/large_stack_frames.rs | 17 +- .../clippy/tests/ui/large_stack_frames.stderr | 64 +- .../tests/ui/legacy_numeric_constants.fixed | 117 + .../tests/ui/legacy_numeric_constants.rs | 117 + .../tests/ui/legacy_numeric_constants.stderr | 184 + .../ui/legacy_numeric_constants_unfixable.rs | 78 + .../legacy_numeric_constants_unfixable.stderr | 83 + src/tools/clippy/tests/ui/len_zero.fixed | 37 + src/tools/clippy/tests/ui/len_zero.rs | 37 + src/tools/clippy/tests/ui/len_zero.stderr | 20 +- .../clippy/tests/ui/let_and_return.fixed | 7 + src/tools/clippy/tests/ui/let_and_return.rs | 7 + src/tools/clippy/tests/ui/let_unit.fixed | 18 + src/tools/clippy/tests/ui/let_unit.rs | 18 + src/tools/clippy/tests/ui/let_unit.stderr | 21 +- .../tests/ui/manual_assert.edition2018.fixed | 8 + .../tests/ui/manual_assert.edition2018.stderr | 11 +- .../tests/ui/manual_assert.edition2021.fixed | 8 + .../tests/ui/manual_assert.edition2021.stderr | 11 +- src/tools/clippy/tests/ui/manual_assert.rs | 10 + src/tools/clippy/tests/ui/manual_clamp.fixed | 279 +- src/tools/clippy/tests/ui/manual_clamp.rs | 333 +- src/tools/clippy/tests/ui/manual_clamp.stderr | 188 +- .../ui/manual_saturating_arithmetic.fixed | 2 +- .../tests/ui/manual_saturating_arithmetic.rs | 2 +- .../tests/ui/manual_swap_auto_fix.fixed | 57 + .../clippy/tests/ui/manual_swap_auto_fix.rs | 72 + .../tests/ui/manual_swap_auto_fix.stderr | 88 + .../tests/ui/manual_unwrap_or_default.fixed | 45 + .../tests/ui/manual_unwrap_or_default.rs | 48 + .../tests/ui/manual_unwrap_or_default.stderr | 12 +- src/tools/clippy/tests/ui/map_clone.fixed | 24 + src/tools/clippy/tests/ui/map_clone.rs | 24 + .../ui/missing_const_for_fn/could_be_const.rs | 2 +- .../ui/missing_transmute_annotations.fixed | 78 + .../tests/ui/missing_transmute_annotations.rs | 78 + .../ui/missing_transmute_annotations.stderr | 76 + .../clippy/tests/ui/mixed_attributes_style.rs | 60 + .../tests/ui/mixed_attributes_style.stderr | 41 +- .../auxiliary/submodule.rs | 9 + .../ui/mixed_attributes_style/global_allow.rs | 7 + .../mixed_attributes_style/mod_declaration.rs | 3 + .../mod_declaration.stderr | 14 + .../clippy/tests/ui/ptr_cast_constness.fixed | 7 +- .../clippy/tests/ui/ptr_cast_constness.rs | 7 +- .../clippy/tests/ui/ptr_cast_constness.stderr | 14 +- src/tools/clippy/tests/ui/question_mark.fixed | 34 + src/tools/clippy/tests/ui/question_mark.rs | 36 + .../clippy/tests/ui/question_mark.stderr | 10 +- .../tests/ui/suspicious_arithmetic_impl.rs | 1 + .../ui/suspicious_arithmetic_impl.stderr | 18 +- .../tests/ui/suspicious_else_formatting.rs | 28 + src/tools/clippy/tests/ui/transmute.rs | 7 +- src/tools/clippy/tests/ui/transmute.stderr | 72 +- .../clippy/tests/ui/transmute_collection.rs | 1 + .../tests/ui/transmute_collection.stderr | 36 +- .../tests/ui/transmute_float_to_int.fixed | 1 + .../clippy/tests/ui/transmute_float_to_int.rs | 1 + .../tests/ui/transmute_float_to_int.stderr | 12 +- .../tests/ui/transmute_int_to_char.fixed | 1 + .../clippy/tests/ui/transmute_int_to_char.rs | 1 + .../tests/ui/transmute_int_to_char.stderr | 4 +- .../ui/transmute_int_to_char_no_std.fixed | 1 + .../tests/ui/transmute_int_to_char_no_std.rs | 1 + .../ui/transmute_int_to_char_no_std.stderr | 4 +- .../tests/ui/transmute_int_to_non_zero.fixed | 1 + .../tests/ui/transmute_int_to_non_zero.rs | 1 + .../tests/ui/transmute_int_to_non_zero.stderr | 20 +- .../clippy/tests/ui/transmute_null_to_fn.rs | 2 +- .../tests/ui/transmute_ptr_to_ptr.fixed | 2 +- .../clippy/tests/ui/transmute_ptr_to_ptr.rs | 2 +- .../tests/ui/transmute_ptr_to_ref.fixed | 6 +- .../clippy/tests/ui/transmute_ptr_to_ref.rs | 6 +- .../tests/ui/transmute_ptr_to_ref.stderr | 44 +- .../clippy/tests/ui/transmute_ref_to_ref.rs | 2 +- .../tests/ui/transmute_ref_to_ref_no_std.rs | 2 +- .../tests/ui/transmute_undefined_repr.rs | 7 +- .../tests/ui/transmute_undefined_repr.stderr | 24 +- .../transmutes_expressible_as_ptr_casts.fixed | 2 +- .../ui/transmutes_expressible_as_ptr_casts.rs | 2 +- src/tools/clippy/tests/ui/transmuting_null.rs | 2 +- .../clippy/tests/ui/type_id_on_box.fixed | 24 +- src/tools/clippy/tests/ui/type_id_on_box.rs | 24 +- .../clippy/tests/ui/type_id_on_box.stderr | 33 +- .../tests/ui/type_id_on_box_unfixable.rs | 31 + .../tests/ui/type_id_on_box_unfixable.stderr | 22 + .../clippy/tests/ui/uninhabited_references.rs | 1 + .../tests/ui/uninhabited_references.stderr | 8 +- src/tools/clippy/tests/ui/use_self.fixed | 3 +- src/tools/clippy/tests/ui/use_self.rs | 3 +- src/tools/clippy/tests/ui/use_self.stderr | 86 +- src/tools/clippy/tests/ui/useless_asref.fixed | 18 + src/tools/clippy/tests/ui/useless_asref.rs | 18 + .../clippy/tests/ui/useless_asref.stderr | 36 +- src/tools/clippy/triagebot.toml | 2 +- src/tools/compiletest/src/lib.rs | 4 +- src/tools/compiletest/src/runtest.rs | 14 +- src/tools/miri/Cargo.toml | 1 + src/tools/miri/README.md | 4 +- src/tools/miri/cargo-miri/src/phases.rs | 148 +- src/tools/miri/cargo-miri/src/setup.rs | 6 +- src/tools/miri/cargo-miri/src/util.rs | 7 +- src/tools/miri/ci/ci.sh | 25 +- src/tools/miri/miri-script/src/commands.rs | 23 +- src/tools/miri/rust-version | 2 +- src/tools/miri/src/alloc_addresses/mod.rs | 6 +- .../borrow_tracker/stacked_borrows/stack.rs | 10 +- .../tree_borrows/diagnostics.rs | 8 +- .../src/borrow_tracker/tree_borrows/perms.rs | 14 +- .../src/borrow_tracker/tree_borrows/tree.rs | 42 +- .../borrow_tracker/tree_borrows/tree/tests.rs | 4 +- .../src/borrow_tracker/tree_borrows/unimap.rs | 2 +- src/tools/miri/src/concurrency/thread.rs | 2 +- src/tools/miri/src/diagnostics.rs | 8 +- src/tools/miri/src/helpers.rs | 69 +- src/tools/miri/src/shims/intrinsics/simd.rs | 10 +- src/tools/miri/src/shims/panic.rs | 12 +- src/tools/miri/test-cargo-miri/Cargo.lock | 90 +- src/tools/miri/test-cargo-miri/Cargo.toml | 12 +- .../test-cargo-miri/issue-1760/Cargo.toml | 8 - .../proc-macro-crate/Cargo.toml | 13 + .../{issue-1760 => proc-macro-crate}/build.rs | 0 .../src/lib.rs | 0 src/tools/miri/test-cargo-miri/src/lib.rs | 2 +- .../tests/fail/intrinsics/unchecked_add1.rs | 2 - .../tests/fail/intrinsics/unchecked_add2.rs | 2 - .../tests/fail/intrinsics/unchecked_mul1.rs | 1 - .../tests/fail/intrinsics/unchecked_mul2.rs | 1 - .../tests/fail/intrinsics/unchecked_sub1.rs | 1 - .../tests/fail/intrinsics/unchecked_sub2.rs | 1 - .../fail/provenance/ptr_int_unexposed.rs | 2 +- .../miri/tests/fail/provenance/ptr_invalid.rs | 2 +- .../fail/provenance/strict_provenance_cast.rs | 2 +- .../provenance/strict_provenance_cast.stderr | 6 +- .../fail/stacked_borrows/exposed_only_ro.rs | 4 +- .../tests/pass-dep/shims/posix_memalign.rs | 2 +- .../miri/tests/pass/async-closure-captures.rs | 125 + .../tests/pass/async-closure-captures.stdout | 14 + src/tools/miri/tests/pass/box.stack.stderr | 6 +- .../miri/tests/pass/extern_types.stack.stderr | 6 +- .../miri/tests/pass/portable-simd-ptrs.rs | 4 +- .../miri/tests/pass/ptr_int_from_exposed.rs | 18 +- .../tests/pass/stacked-borrows/int-to-ptr.rs | 6 +- .../stacked-borrows/issue-miri-2389.stderr | 6 +- .../pass/stacked-borrows/unknown-bottom-gc.rs | 2 +- src/tools/miri/tests/ui.rs | 57 +- src/tools/run-make-support/src/cc.rs | 202 + src/tools/run-make-support/src/lib.rs | 95 +- src/tools/run-make-support/src/run.rs | 21 +- src/tools/run-make-support/src/rustc.rs | 18 + .../rust-analyzer/.github/workflows/ci.yaml | 4 +- .../.github/workflows/publish-libs.yaml | 1 + .../.github/workflows/release.yaml | 17 +- src/tools/rust-analyzer/Cargo.lock | 43 +- src/tools/rust-analyzer/Cargo.toml | 11 +- .../rust-analyzer/crates/base-db/src/input.rs | 84 +- .../rust-analyzer/crates/base-db/src/lib.rs | 6 +- .../rust-analyzer/crates/flycheck/src/lib.rs | 8 +- .../crates/flycheck/src/test_runner.rs | 5 +- .../rust-analyzer/crates/hir-def/src/attr.rs | 8 +- .../rust-analyzer/crates/hir-def/src/data.rs | 9 +- .../crates/hir-def/src/generics.rs | 80 +- .../crates/hir-def/src/item_tree/pretty.rs | 2 +- .../crates/hir-def/src/lang_item.rs | 2 +- .../rust-analyzer/crates/hir-def/src/lib.rs | 4 +- .../hir-def/src/macro_expansion_tests/mbe.rs | 85 + .../crates/hir-def/src/nameres.rs | 6 +- .../hir-def/src/nameres/attr_resolution.rs | 2 + .../crates/hir-def/src/nameres/collector.rs | 42 +- .../hir-def/src/nameres/path_resolution.rs | 2 +- .../crates/hir-def/src/nameres/proc_macro.rs | 14 +- .../crates/hir-def/src/resolver.rs | 20 + .../crates/hir-expand/src/attrs.rs | 165 +- .../crates/hir-expand/src/builtin_fn_macro.rs | 4 +- .../crates/hir-expand/src/cfg_process.rs | 12 +- .../rust-analyzer/crates/hir-expand/src/db.rs | 65 +- .../crates/hir-expand/src/declarative.rs | 38 +- .../crates/hir-expand/src/lib.rs | 21 +- .../crates/hir-expand/src/mod_path.rs | 42 +- .../crates/hir-expand/src/proc_macro.rs | 2 +- .../rust-analyzer/crates/hir-ty/Cargo.toml | 3 +- .../crates/hir-ty/src/builder.rs | 34 +- .../crates/hir-ty/src/chalk_db.rs | 13 + .../crates/hir-ty/src/chalk_ext.rs | 14 + .../crates/hir-ty/src/consteval/tests.rs | 27 + .../rust-analyzer/crates/hir-ty/src/db.rs | 16 +- .../crates/hir-ty/src/diagnostics/expr.rs | 17 +- .../diagnostics/match_check/pat_analysis.rs | 84 +- .../crates/hir-ty/src/display.rs | 82 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 159 +- .../crates/hir-ty/src/infer/coerce.rs | 17 + .../crates/hir-ty/src/infer/expr.rs | 39 +- .../crates/hir-ty/src/infer/path.rs | 9 +- .../crates/hir-ty/src/infer/unify.rs | 28 +- .../rust-analyzer/crates/hir-ty/src/layout.rs | 3 + .../rust-analyzer/crates/hir-ty/src/lib.rs | 95 +- .../rust-analyzer/crates/hir-ty/src/lower.rs | 239 +- .../crates/hir-ty/src/mapping.rs | 8 + .../crates/hir-ty/src/method_resolution.rs | 15 +- .../crates/hir-ty/src/mir/eval.rs | 56 +- .../crates/hir-ty/src/mir/eval/shim.rs | 65 +- .../crates/hir-ty/src/mir/monomorphization.rs | 15 +- .../rust-analyzer/crates/hir-ty/src/tests.rs | 2 +- .../hir-ty/src/tests/display_source_code.rs | 2 +- .../crates/hir-ty/src/tests/patterns.rs | 2 +- .../crates/hir-ty/src/tests/regression.rs | 4 +- .../crates/hir-ty/src/tests/simple.rs | 6 +- .../crates/hir-ty/src/tests/traits.rs | 109 + .../rust-analyzer/crates/hir-ty/src/utils.rs | 166 +- .../rust-analyzer/crates/hir/src/display.rs | 33 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 60 +- .../crates/hir/src/term_search/tactics.rs | 4 +- .../src/handlers/extract_function.rs | 2 +- .../src/handlers/generate_delegate_methods.rs | 2 +- .../ide-completion/src/completions/dot.rs | 4 +- .../src/completions/item_list/trait_impl.rs | 8 +- .../ide-completion/src/completions/keyword.rs | 6 +- .../crates/ide-completion/src/context.rs | 2 +- .../crates/ide-completion/src/item.rs | 3 +- .../crates/ide-completion/src/render.rs | 49 +- .../ide-completion/src/render/function.rs | 8 +- .../ide-completion/src/tests/expression.rs | 2 +- .../ide-completion/src/tests/predicate.rs | 4 +- .../ide-completion/src/tests/special.rs | 47 +- .../ide-completion/src/tests/type_pos.rs | 8 +- .../rust-analyzer/crates/ide-db/src/lib.rs | 1 + .../crates/ide-diagnostics/Cargo.toml | 1 + .../src/handlers/missing_match_arms.rs | 27 + .../src/handlers/mutability_errors.rs | 50 +- .../src/handlers/remove_trailing_return.rs | 19 +- .../src/handlers/remove_unnecessary_else.rs | 23 +- .../src/handlers/unlinked_file.rs | 7 +- .../src/handlers/unused_variables.rs | 28 +- .../crates/ide-diagnostics/src/lib.rs | 32 +- .../crates/ide-diagnostics/src/tests.rs | 4 + src/tools/rust-analyzer/crates/ide/Cargo.toml | 1 + .../rust-analyzer/crates/ide/src/doc_links.rs | 16 +- .../crates/ide/src/doc_links/tests.rs | 30 +- .../crates/ide/src/file_structure.rs | 13 +- .../crates/ide/src/goto_implementation.rs | 71 + .../rust-analyzer/crates/ide/src/hover.rs | 1 + .../crates/ide/src/hover/render.rs | 3 + .../crates/ide/src/hover/tests.rs | 175 +- .../crates/ide/src/inlay_hints.rs | 96 +- .../crates/ide/src/inlay_hints/adjustment.rs | 1 - .../crates/ide/src/inlay_hints/bind_pat.rs | 9 +- .../ide/src/inlay_hints/binding_mode.rs | 2 - .../crates/ide/src/inlay_hints/chaining.rs | 1 - .../ide/src/inlay_hints/closing_brace.rs | 1 - .../ide/src/inlay_hints/closure_captures.rs | 5 - .../crates/ide/src/inlay_hints/closure_ret.rs | 1 - .../ide/src/inlay_hints/discriminant.rs | 1 - .../ide/src/inlay_hints/fn_lifetime_fn.rs | 3 - .../ide/src/inlay_hints/implicit_drop.rs | 1 - .../ide/src/inlay_hints/implicit_static.rs | 1 - .../crates/ide/src/inlay_hints/param_name.rs | 1 - .../ide/src/inlay_hints/range_exclusive.rs | 1 - src/tools/rust-analyzer/crates/ide/src/lib.rs | 28 +- .../crates/ide/src/static_index.rs | 1 + .../rust-analyzer/crates/ide/src/status.rs | 10 +- .../ide/src/syntax_highlighting/highlight.rs | 48 +- .../ide/src/syntax_highlighting/html.rs | 1 + .../ide/src/syntax_highlighting/tags.rs | 9 +- .../test_data/highlight_assoc_functions.html | 7 +- .../test_data/highlight_attributes.html | 8 +- .../test_data/highlight_block_mod_items.html | 3 +- .../test_data/highlight_const.html | 83 + .../test_data/highlight_crate_root.html | 9 +- .../test_data/highlight_default_library.html | 3 +- .../test_data/highlight_doctest.html | 15 +- .../test_data/highlight_extern_crate.html | 1 + .../test_data/highlight_general.html | 57 +- .../test_data/highlight_injection.html | 1 + .../test_data/highlight_keywords.html | 1 + .../test_data/highlight_lifetimes.html | 1 + .../test_data/highlight_macros.html | 1 + .../highlight_module_docs_inline.html | 1 + .../highlight_module_docs_outline.html | 1 + .../test_data/highlight_operators.html | 1 + .../test_data/highlight_rainbow.html | 1 + .../test_data/highlight_strings.html | 5 +- .../test_data/highlight_unsafe.html | 11 +- .../ide/src/syntax_highlighting/tests.rs | 76 +- .../crates/ide/src/test_explorer.rs | 68 +- .../rust-analyzer/crates/ide/src/typing.rs | 33 +- .../crates/load-cargo/Cargo.toml | 1 + .../crates/load-cargo/src/lib.rs | 4 +- .../rust-analyzer/crates/mbe/src/benchmark.rs | 20 +- .../rust-analyzer/crates/mbe/src/expander.rs | 45 +- .../crates/mbe/src/expander/matcher.rs | 160 +- .../crates/mbe/src/expander/transcriber.rs | 99 +- src/tools/rust-analyzer/crates/mbe/src/lib.rs | 64 +- .../rust-analyzer/crates/mbe/src/parser.rs | 100 +- .../crates/mbe/src/syntax_bridge.rs | 56 +- .../crates/mbe/src/to_parser_input.rs | 6 +- .../rust-analyzer/crates/mbe/src/tt_iter.rs | 14 +- .../parser/src/grammar/expressions/atom.rs | 58 +- .../inline/err/0034_match_arms_recovery.rast | 113 + .../inline/err/0034_match_arms_recovery.rs | 11 + .../rust-analyzer/crates/paths/Cargo.toml | 6 +- .../rust-analyzer/crates/paths/src/lib.rs | 242 +- .../crates/proc-macro-api/Cargo.toml | 2 +- .../crates/proc-macro-api/src/lib.rs | 5 +- .../crates/proc-macro-api/src/msg.rs | 12 +- .../crates/proc-macro-api/src/msg/flat.rs | 2 - .../crates/proc-macro-api/src/process.rs | 4 +- .../crates/proc-macro-srv/src/dylib.rs | 47 +- .../crates/proc-macro-srv/src/lib.rs | 26 +- .../crates/proc-macro-srv/src/proc_macros.rs | 2 +- .../proc-macro-srv/src/server/token_stream.rs | 4 +- .../crates/proc-macro-srv/src/tests/mod.rs | 16 +- .../crates/project-model/Cargo.toml | 3 +- .../crates/project-model/src/build_scripts.rs | 19 +- .../project-model/src/cargo_workspace.rs | 21 +- .../crates/project-model/src/lib.rs | 4 +- .../crates/project-model/src/project_json.rs | 20 +- .../crates/project-model/src/sysroot.rs | 6 +- .../crates/project-model/src/tests.rs | 20 +- .../crates/project-model/src/workspace.rs | 86 +- .../crates/rust-analyzer/Cargo.toml | 3 +- .../crates/rust-analyzer/src/bin/main.rs | 2 +- .../rust-analyzer/src/cli/analysis_stats.rs | 5 +- .../rust-analyzer/src/cli/diagnostics.rs | 2 +- .../crates/rust-analyzer/src/cli/flags.rs | 1 + .../crates/rust-analyzer/src/cli/lsif.rs | 2 +- .../rust-analyzer/src/cli/rustc_tests.rs | 2 +- .../crates/rust-analyzer/src/cli/scip.rs | 12 +- .../crates/rust-analyzer/src/config.rs | 49 +- .../rust-analyzer/src/diagnostics/to_proto.rs | 5 +- .../crates/rust-analyzer/src/global_state.rs | 18 +- .../src/handlers/notification.rs | 2 +- .../rust-analyzer/src/handlers/request.rs | 31 +- .../src/integrated_benchmarks.rs | 6 +- .../crates/rust-analyzer/src/lsp/ext.rs | 4 +- .../rust-analyzer/src/lsp/semantic_tokens.rs | 3 +- .../crates/rust-analyzer/src/lsp/to_proto.rs | 107 +- .../crates/rust-analyzer/src/main_loop.rs | 36 +- .../crates/rust-analyzer/src/reload.rs | 92 +- .../crates/rust-analyzer/tests/crate_graph.rs | 2 +- .../rust-analyzer/tests/slow-tests/main.rs | 13 +- .../rust-analyzer/tests/slow-tests/support.rs | 6 +- .../rust-analyzer/tests/slow-tests/testdir.rs | 22 +- .../rust-analyzer/crates/span/src/lib.rs | 50 + .../rust-analyzer/crates/syntax/Cargo.toml | 4 - .../rust-analyzer/crates/syntax/rust.ungram | 2 +- .../crates/syntax/src/ast/generated/nodes.rs | 3536 ++++++++--------- .../crates/syntax/src/ast/generated/tokens.rs | 80 +- .../rust-analyzer/crates/syntax/src/tests.rs | 25 +- .../crates/test-fixture/src/lib.rs | 25 +- .../rust-analyzer/crates/toolchain/Cargo.toml | 3 +- .../rust-analyzer/crates/toolchain/src/lib.rs | 43 +- src/tools/rust-analyzer/crates/tt/Cargo.toml | 5 +- src/tools/rust-analyzer/crates/tt/src/lib.rs | 11 +- .../crates/vfs-notify/src/lib.rs | 6 +- src/tools/rust-analyzer/crates/vfs/src/lib.rs | 57 +- .../rust-analyzer/crates/vfs/src/loader.rs | 2 +- .../rust-analyzer/crates/vfs/src/vfs_path.rs | 2 +- .../rust-analyzer/docs/dev/lsp-extensions.md | 14 +- .../docs/user/generated_config.adoc | 5 + .../rust-analyzer/editors/code/package.json | 9 + .../rust-analyzer/editors/code/src/config.ts | 25 +- .../rust-analyzer/editors/code/src/debug.ts | 6 +- .../rust-analyzer/editors/code/src/lsp_ext.ts | 6 +- .../rust-analyzer/editors/code/src/run.ts | 28 +- .../rust-analyzer/editors/code/src/tasks.ts | 50 +- .../editors/code/src/test_explorer.ts | 63 +- src/tools/rust-analyzer/xtask/Cargo.toml | 4 + src/tools/rust-analyzer/xtask/src/codegen.rs | 2 + .../src/codegen/grammar.rs} | 72 +- .../src/codegen/grammar}/ast_src.rs | 0 src/tools/rust-analyzer/xtask/src/dist.rs | 21 +- src/tools/rust-analyzer/xtask/src/flags.rs | 141 +- src/tools/rust-analyzer/xtask/src/install.rs | 16 +- src/tools/rust-installer/src/tarballer.rs | 12 +- src/tools/rustdoc-js/.eslintrc.js | 2 +- src/tools/rustdoc-js/tester.js | 99 +- src/tools/rustfmt/src/patterns.rs | 91 +- src/tools/rustfmt/tests/source/mut_ref.rs | 10 + src/tools/rustfmt/tests/target/mut_ref.rs | 10 + .../tidy/src/allowed_run_make_makefiles.txt | 4 - src/tools/tidy/src/deps.rs | 5 +- src/tools/tidy/src/issues.txt | 19 +- src/tools/tidy/src/rustdoc_css_themes.rs | 7 +- src/tools/tidy/src/ui_tests.rs | 4 +- tests/assembly/is_aligned.rs | 2 +- tests/assembly/x86_64-cmp.rs | 51 + tests/auxiliary/rust_test_helpers.c | 24 + .../item-collection/implicit-panic-call.rs | 14 +- tests/codegen/cast-target-abi.rs | 280 ++ tests/codegen/cffi/ffi-out-of-bounds-loads.rs | 28 +- .../enum/uninhabited_enum_default_branch.rs | 24 - .../enum/unreachable_enum_default_branch.rs | 43 + tests/codegen/inherit_overflow.rs | 2 +- tests/codegen/intrinsics/three_way_compare.rs | 47 + .../no-redundant-item-monomorphization.rs | 33 + ...itanium-cxx-abi-method-secondary-typeid.rs | 22 + tests/codegen/unchecked_shifts.rs | 49 +- tests/codegen/vec_pop_push_noop.rs | 6 +- tests/coverage/unreachable.cov-map | 12 +- tests/debuginfo/path.rs | 29 + .../incremental/hashes/function_interfaces.rs | 8 +- tests/incremental/hashes/inherent_impls.rs | 8 +- ...fg-pre-optimizations.after.panic-abort.mir | 2 - ...g-pre-optimizations.after.panic-unwind.mir | 2 - ...c_abort.main.AbortUnwindingCalls.after.mir | 2 - .../mir-opt/box_expr.main.ElaborateDrops.diff | 2 - ...await.b-{closure#0}.coroutine_resume.0.mir | 8 +- .../custom/as_cast.int_to_ptr.built.after.mir | 2 +- .../string.foo.PreCodegen.after.mir | 0 .../match}/deref-patterns/string.rs | 0 ....match_tuple.SimplifyCfg-initial.after.mir | 0 .../{ => building/match}/exponential_or.rs | 0 ...se_edges.full_tested_match.built.after.mir | 0 ...e_edges.full_tested_match2.built.after.mir | 0 .../match_false_edges.main.built.after.mir | 2 +- .../building/{ => match}/match_false_edges.rs | 0 .../simple_match.match_bool.built.after.mir | 0 .../building/{ => match}/simple_match.rs | 0 ....constant_eq.SimplifyCfg-initial.after.mir | 118 + ...joint_ranges.SimplifyCfg-initial.after.mir | 88 + .../mir-opt/building/match/sort_candidates.rs | 41 + ...e_out.move_out_by_subslice.built.after.mir | 6 +- ...move_out.move_out_from_end.built.after.mir | 6 +- ...motion_extern_static.FOO.PromoteTemps.diff | 2 - .../const_prop/address_of_pair.fn0.GVN.diff | 2 - ...for_slices.main.GVN.32bit.panic-abort.diff | 6 +- ...or_slices.main.GVN.32bit.panic-unwind.diff | 6 +- ...for_slices.main.GVN.64bit.panic-abort.diff | 6 +- ...or_slices.main.GVN.64bit.panic-unwind.diff | 6 +- .../boxes.main.GVN.panic-abort.diff | 2 - .../boxes.main.GVN.panic-unwind.diff | 2 - .../const_prop/indirect_mutation.bar.GVN.diff | 2 - .../const_prop/invalid_constant.main.GVN.diff | 12 +- .../invalid_constant.main.RemoveZsts.diff | 12 +- .../mutable_variable_no_prop.main.GVN.diff | 2 - ...pose_provenance.main.GVN.panic-abort.diff} | 2 +- ...ose_provenance.main.GVN.panic-unwind.diff} | 2 +- ...ddress.rs => pointer_expose_provenance.rs} | 4 +- .../const_prop/reify_fn_ptr.main.GVN.diff | 4 +- tests/mir-opt/const_prop/reify_fn_ptr.rs | 4 +- .../transmute.from_char.GVN.32bit.diff | 2 - .../transmute.from_char.GVN.64bit.diff | 2 - .../transmute.invalid_bool.GVN.32bit.diff | 2 - .../transmute.invalid_bool.GVN.64bit.diff | 2 - .../transmute.invalid_char.GVN.32bit.diff | 2 - .../transmute.invalid_char.GVN.64bit.diff | 2 - .../transmute.less_as_i8.GVN.32bit.diff | 2 - .../transmute.less_as_i8.GVN.64bit.diff | 2 - ...mute.undef_union_as_integer.GVN.32bit.diff | 2 - ...mute.undef_union_as_integer.GVN.64bit.diff | 2 - .../transmute.unreachable_box.GVN.32bit.diff | 2 - .../transmute.unreachable_box.GVN.64bit.diff | 2 - ...ransmute.unreachable_direct.GVN.32bit.diff | 2 - ...ransmute.unreachable_direct.GVN.64bit.diff | 2 - .../transmute.unreachable_mut.GVN.32bit.diff | 2 - .../transmute.unreachable_mut.GVN.64bit.diff | 2 - .../transmute.unreachable_ref.GVN.32bit.diff | 2 - .../transmute.unreachable_ref.GVN.64bit.diff | 2 - .../transmute.valid_char.GVN.32bit.diff | 2 - .../transmute.valid_char.GVN.64bit.diff | 2 - ...reborrow.demiraw.CopyProp.panic-abort.diff | 4 +- ...eborrow.demiraw.CopyProp.panic-unwind.diff | 4 +- .../reborrow.miraw.CopyProp.panic-abort.diff | 4 +- .../reborrow.miraw.CopyProp.panic-unwind.diff | 4 +- ...n.DataflowConstProp.32bit.panic-abort.diff | 38 +- ....DataflowConstProp.32bit.panic-unwind.diff | 38 +- ...n.DataflowConstProp.64bit.panic-abort.diff | 38 +- ....DataflowConstProp.64bit.panic-unwind.diff | 38 +- ...oxed_slice.main.GVN.32bit.panic-abort.diff | 38 +- ...xed_slice.main.GVN.32bit.panic-unwind.diff | 38 +- ...oxed_slice.main.GVN.64bit.panic-abort.diff | 38 +- ...xed_slice.main.GVN.64bit.panic-unwind.diff | 38 +- ...tr.main.DataflowConstProp.panic-abort.diff | 8 +- ...r.main.DataflowConstProp.panic-unwind.diff | 8 +- ...ute.from_char.DataflowConstProp.32bit.diff | 2 - ...ute.from_char.DataflowConstProp.64bit.diff | 2 - ....invalid_bool.DataflowConstProp.32bit.diff | 2 - ....invalid_bool.DataflowConstProp.64bit.diff | 2 - ....invalid_char.DataflowConstProp.32bit.diff | 2 - ....invalid_char.DataflowConstProp.64bit.diff | 2 - ...te.less_as_i8.DataflowConstProp.32bit.diff | 2 - ...te.less_as_i8.DataflowConstProp.64bit.diff | 2 - ...on_as_integer.DataflowConstProp.32bit.diff | 2 - ...on_as_integer.DataflowConstProp.64bit.diff | 2 - ...reachable_box.DataflowConstProp.32bit.diff | 2 - ...reachable_box.DataflowConstProp.64bit.diff | 2 - ...chable_direct.DataflowConstProp.32bit.diff | 2 - ...chable_direct.DataflowConstProp.64bit.diff | 2 - ...reachable_mut.DataflowConstProp.32bit.diff | 2 - ...reachable_mut.DataflowConstProp.64bit.diff | 2 - ...reachable_ref.DataflowConstProp.32bit.diff | 2 - ...reachable_ref.DataflowConstProp.64bit.diff | 2 - ...te.valid_char.DataflowConstProp.32bit.diff | 2 - ...te.valid_char.DataflowConstProp.64bit.diff | 2 - ...r_to_int.DeadStoreElimination-initial.diff | 4 +- .../provenance_soundness.rs | 4 +- ...in.DestinationPropagation.panic-abort.diff | 6 +- ...n.DestinationPropagation.panic-unwind.diff | 6 +- .../gvn.dereferences.GVN.panic-abort.diff | 10 +- .../gvn.dereferences.GVN.panic-unwind.diff | 10 +- tests/mir-opt/gvn.slices.GVN.panic-abort.diff | 6 +- .../mir-opt/gvn.slices.GVN.panic-unwind.diff | 6 +- ...xpression_elimination.GVN.panic-abort.diff | 12 +- ...pression_elimination.GVN.panic-unwind.diff | 12 +- .../gvn.wide_ptr_integer.GVN.panic-abort.diff | 6 +- ...gvn.wide_ptr_integer.GVN.panic-unwind.diff | 6 +- .../gvn_uninhabited.f.GVN.panic-abort.diff | 4 +- .../gvn_uninhabited.f.GVN.panic-unwind.diff | 4 +- .../asm_unwind.main.Inline.panic-unwind.diff | 2 - ...ine_coroutine.main.Inline.panic-abort.diff | 8 +- ...ne_coroutine.main.Inline.panic-unwind.diff | 8 +- ...inline_instruction_set.default.Inline.diff | 2 - .../inline_shims.drop.Inline.panic-abort.diff | 20 +- ...inline_shims.drop.Inline.panic-unwind.diff | 12 +- tests/mir-opt/inline/unchecked_shifts.rs | 19 +- ...hl_unsigned_bigger.Inline.panic-abort.diff | 36 - ...l_unsigned_bigger.Inline.panic-unwind.diff | 36 - ...ed_bigger.PreCodegen.after.panic-abort.mir | 22 - ...d_bigger.PreCodegen.after.panic-unwind.mir | 22 - ...l_unsigned_smaller.Inline.panic-abort.diff | 13 +- ..._unsigned_smaller.Inline.panic-unwind.diff | 13 +- ...d_smaller.PreCodegen.after.panic-abort.mir | 13 +- ..._smaller.PreCodegen.after.panic-unwind.mir | 13 +- ..._shr_signed_bigger.Inline.panic-abort.diff | 8 +- ...shr_signed_bigger.Inline.panic-unwind.diff | 8 +- ...ed_bigger.PreCodegen.after.panic-abort.mir | 8 +- ...d_bigger.PreCodegen.after.panic-unwind.mir | 8 +- ...shr_signed_smaller.Inline.panic-abort.diff | 41 - ...hr_signed_smaller.Inline.panic-unwind.diff | 41 - ...d_smaller.PreCodegen.after.panic-abort.mir | 27 - ..._smaller.PreCodegen.after.panic-unwind.mir | 27 - ...d.unwrap_unchecked.Inline.panic-abort.diff | 14 +- ....unwrap_unchecked.Inline.panic-unwind.diff | 14 +- ...unchecked.PreCodegen.after.panic-abort.mir | 23 +- ...nchecked.PreCodegen.after.panic-unwind.mir | 23 +- ...y.run2-{closure#0}.Inline.panic-abort.diff | 24 +- ....run2-{closure#0}.Inline.panic-unwind.diff | 24 +- tests/mir-opt/instsimplify/ub_check.rs | 16 + ...b_check.unwrap_unchecked.InstSimplify.diff | 53 + ....AbortUnwindingCalls.after.panic-abort.mir | 2 - ...AbortUnwindingCalls.after.panic-unwind.mir | 2 - ...test.ElaborateDrops.before.panic-abort.mir | 8 +- ...est.ElaborateDrops.before.panic-unwind.mir | 8 +- .../mir-opt/issue_72181.main.built.after.mir | 2 - .../issue_72181_1.main.built.after.mir | 2 - ...e_75439.foo.MatchBranchSimplification.diff | 6 +- ...mutable_ref.JumpThreading.panic-abort.diff | 2 - ...utable_ref.JumpThreading.panic-unwind.diff | 2 - ...len_raw.NormalizeArrayLen.panic-abort.diff | 2 - ...en_raw.NormalizeArrayLen.panic-unwind.diff | 2 - ...cs.assume.LowerIntrinsics.panic-abort.diff | 2 - ...s.assume.LowerIntrinsics.panic-unwind.diff | 2 - ...erlapping.LowerIntrinsics.panic-abort.diff | 2 - ...rlapping.LowerIntrinsics.panic-unwind.diff | 2 - ...primitive.LowerIntrinsics.panic-abort.diff | 2 - ...rimitive.LowerIntrinsics.panic-unwind.diff | 2 - ...inhabited.LowerIntrinsics.panic-abort.diff | 2 - ...nhabited.LowerIntrinsics.panic-unwind.diff | 2 - tests/mir-opt/lower_intrinsics.rs | 21 +- ...pare_char.LowerIntrinsics.panic-abort.diff | 34 + ...are_char.LowerIntrinsics.panic-unwind.diff | 34 + ...re_signed.LowerIntrinsics.panic-abort.diff | 31 + ...e_signed.LowerIntrinsics.panic-unwind.diff | 31 + ..._unsigned.LowerIntrinsics.panic-abort.diff | 34 + ...unsigned.LowerIntrinsics.panic-unwind.diff | 34 + ...inhabited.LowerIntrinsics.panic-abort.diff | 2 - ...nhabited.LowerIntrinsics.panic-unwind.diff | 2 - ...e_ref_dst.LowerIntrinsics.panic-abort.diff | 2 - ..._ref_dst.LowerIntrinsics.panic-unwind.diff | 2 - ...inhabited.LowerIntrinsics.panic-abort.diff | 2 - ...nhabited.LowerIntrinsics.panic-unwind.diff | 2 - ...unchecked.LowerIntrinsics.panic-abort.diff | 183 +- ...nchecked.LowerIntrinsics.panic-unwind.diff | 183 +- ...reachable.LowerIntrinsics.panic-abort.diff | 2 - ...eachable.LowerIntrinsics.panic-unwind.diff | 2 - ...ve_string.LowerIntrinsics.panic-abort.diff | 2 - ...e_string.LowerIntrinsics.panic-unwind.diff | 2 - ...fg-initial.after-ElaborateDrops.after.diff | 8 +- ...fg-initial.after-ElaborateDrops.after.diff | 8 +- ...ch_test.main.SimplifyCfg-initial.after.mir | 106 - tests/mir-opt/match_test.rs | 19 - ...ecked_ops.checked_shl.PreCodegen.after.mir | 10 +- tests/mir-opt/pre-codegen/derived_ord.rs | 9 + ....{impl#0}-partial_cmp.PreCodegen.after.mir | 78 + ...witch_targets.ub_if_b.PreCodegen.after.mir | 17 +- .../loops.int_range.PreCodegen.after.mir | 4 +- ...m_replace.PreCodegen.after.panic-abort.mir | 20 +- ..._replace.PreCodegen.after.panic-unwind.mir | 20 +- ...ward_loop.PreCodegen.after.panic-abort.mir | 4 +- ...ard_loop.PreCodegen.after.panic-unwind.mir | 4 +- ...iter_next.PreCodegen.after.panic-abort.mir | 4 +- ...ter_next.PreCodegen.after.panic-unwind.mir | 4 +- ...mut_range.PreCodegen.after.panic-abort.mir | 2 - ...ut_range.PreCodegen.after.panic-unwind.mir | 2 - ...ated_loop.PreCodegen.after.panic-abort.mir | 48 +- ...ted_loop.PreCodegen.after.panic-unwind.mir | 48 +- ...ward_loop.PreCodegen.after.panic-abort.mir | 44 +- ...ard_loop.PreCodegen.after.panic-unwind.mir | 44 +- ...ange_loop.PreCodegen.after.panic-abort.mir | 4 +- ...nge_loop.PreCodegen.after.panic-unwind.mir | 4 +- ...erse_loop.PreCodegen.after.panic-abort.mir | 50 +- ...rse_loop.PreCodegen.after.panic-unwind.mir | 50 +- ...raw_then_mut_shr.ReferencePropagation.diff | 2 - ...gation_const_ptr.ReferencePropagation.diff | 258 +- ...pagation_mut_ptr.ReferencePropagation.diff | 232 +- ...ique_with_copies.ReferencePropagation.diff | 4 - ...fg-pre-optimizations.after.panic-abort.mir | 10 +- ...g-pre-optimizations.after.panic-unwind.mir | 10 +- ...yCfg-after-unreachable-enum-branching.diff | 33 + tests/mir-opt/simplify_dead_blocks.rs | 52 + ...yCfg-after-uninhabited-enum-branching.diff | 25 - .../simplify_duplicate_unreachable_blocks.rs | 31 - ...nce.SimplifyLocals-before-const-prop.diff} | 8 +- tests/mir-opt/simplify_locals.rs | 6 +- ...s.t1.SimplifyLocals-before-const-prop.diff | 2 - ...s.t2.SimplifyLocals-before-const-prop.diff | 2 - ...s.t3.SimplifyLocals-before-const-prop.diff | 2 - ...s.t4.SimplifyLocals-before-const-prop.diff | 2 - ....unions.ScalarReplacementOfAggregates.diff | 2 - .../tls_access.main.PreCodegen.after.mir | 6 +- ...ocess_never.SimplifyLocals-final.after.mir | 2 - ...rocess_void.SimplifyLocals-final.after.mir | 2 - ...fallthrough.UnreachableEnumBranching.diff} | 4 +- ...fallthrough.UnreachableEnumBranching.diff} | 4 +- .../uninhabited_fallthrough_elimination.rs | 4 +- ...d_access.bar.SimplifyCfg-initial.after.mir | 2 - ...UnreachableEnumBranching.panic-abort.diff} | 4 +- ...nreachableEnumBranching.panic-unwind.diff} | 4 +- ...UnreachableEnumBranching.panic-abort.diff} | 4 +- ...nreachableEnumBranching.panic-unwind.diff} | 4 +- ...UnreachableEnumBranching.panic-abort.diff} | 4 +- ...nreachableEnumBranching.panic-unwind.diff} | 4 +- ...UnreachableEnumBranching.panic-abort.diff} | 4 +- ...nreachableEnumBranching.panic-unwind.diff} | 4 +- ...UnreachableEnumBranching.panic-abort.diff} | 4 +- ...nreachableEnumBranching.panic-unwind.diff} | 4 +- ...UnreachableEnumBranching.panic-abort.diff} | 4 +- ...nreachableEnumBranching.panic-unwind.diff} | 4 +- ...UnreachableEnumBranching.panic-abort.diff} | 6 +- ...nreachableEnumBranching.panic-unwind.diff} | 6 +- ...UnreachableEnumBranching.panic-abort.diff} | 6 +- ...nreachableEnumBranching.panic-unwind.diff} | 6 +- ...UnreachableEnumBranching.panic-abort.diff} | 6 +- ...nreachableEnumBranching.panic-unwind.diff} | 6 +- ...ching.rs => unreachable_enum_branching.rs} | 40 +- ...UnreachableEnumBranching.panic-abort.diff} | 4 +- ...nreachableEnumBranching.panic-unwind.diff} | 4 +- .../arguments-non-c-like-enum/Makefile | 8 - .../arguments-non-c-like-enum/rmake.rs | 20 + tests/run-make/hir-tree/Makefile | 8 - tests/run-make/hir-tree/input.rs | 3 - tests/run-make/issue-7349/Makefile | 11 - tests/run-make/issue-7349/foo.rs | 22 - .../non-unicode-env/non_unicode_env.rs | 3 + .../non-unicode-env/non_unicode_env.stderr | 10 + tests/run-make/non-unicode-env/rmake.rs | 14 + tests/run-make/remap-path-prefix/Makefile | 13 +- tests/run-make/split-debuginfo/Makefile | 4 +- tests/run-make/version/Makefile | 6 - tests/rustdoc-gui/anchors.goml | 2 +- tests/rustdoc-gui/code-color.goml | 2 +- tests/rustdoc-gui/codeblock-tooltip.goml | 2 +- tests/rustdoc-gui/cursor.goml | 2 +- .../docblock-code-block-line-number.goml | 2 +- tests/rustdoc-gui/docblock-table.goml | 2 +- tests/rustdoc-gui/escape-key.goml | 2 +- tests/rustdoc-gui/globals.goml | 2 +- tests/rustdoc-gui/go-to-collapsed-elem.goml | 4 +- tests/rustdoc-gui/headers-color.goml | 2 +- tests/rustdoc-gui/headings-anchor.goml | 8 +- tests/rustdoc-gui/headings.goml | 10 +- tests/rustdoc-gui/help-page.goml | 8 +- tests/rustdoc-gui/highlight-colors.goml | 4 +- tests/rustdoc-gui/item-decl-colors.goml | 4 +- .../item-decl-comment-highlighting.goml | 4 +- tests/rustdoc-gui/item-info-alignment.goml | 4 +- tests/rustdoc-gui/item-info.goml | 4 +- tests/rustdoc-gui/javascript-disabled.goml | 14 + tests/rustdoc-gui/jump-to-def-background.goml | 17 +- tests/rustdoc-gui/label-next-to-symbol.goml | 12 +- tests/rustdoc-gui/links-color.goml | 4 +- tests/rustdoc-gui/notable-trait.goml | 20 +- tests/rustdoc-gui/pocket-menu.goml | 2 +- tests/rustdoc-gui/run-on-hover.goml | 2 +- tests/rustdoc-gui/rust-logo.goml | 2 +- tests/rustdoc-gui/scrape-examples-color.goml | 6 +- tests/rustdoc-gui/scrape-examples-toggle.goml | 2 +- tests/rustdoc-gui/search-corrections.goml | 10 +- tests/rustdoc-gui/search-error.goml | 2 +- tests/rustdoc-gui/search-filter.goml | 2 +- tests/rustdoc-gui/search-form-elements.goml | 4 +- tests/rustdoc-gui/search-keyboard.goml | 2 +- tests/rustdoc-gui/search-no-result.goml | 2 +- tests/rustdoc-gui/search-reexport.goml | 4 +- tests/rustdoc-gui/search-result-color.goml | 246 +- tests/rustdoc-gui/search-result-display.goml | 4 +- .../search-result-impl-disambiguation.goml | 4 +- tests/rustdoc-gui/search-result-keyword.goml | 2 +- .../search-tab-change-title-fn-sig.goml | 10 +- tests/rustdoc-gui/search-tab.goml | 12 +- ...setting-auto-hide-content-large-items.goml | 2 +- .../setting-auto-hide-item-methods-docs.goml | 2 +- ...tting-auto-hide-trait-implementations.goml | 2 +- .../setting-go-to-only-result.goml | 4 +- tests/rustdoc-gui/settings.goml | 2 +- tests/rustdoc-gui/sidebar-links-color.goml | 4 +- tests/rustdoc-gui/sidebar-mobile.goml | 2 +- .../sidebar-source-code-display.goml | 4 +- tests/rustdoc-gui/sidebar-source-code.goml | 2 +- tests/rustdoc-gui/sidebar.goml | 2 +- tests/rustdoc-gui/source-code-page.goml | 6 +- tests/rustdoc-gui/stab-badge.goml | 2 +- tests/rustdoc-gui/target.goml | 2 +- tests/rustdoc-gui/toggle-docs.goml | 2 +- .../rustdoc-gui/type-declation-overflow.goml | 8 +- tests/rustdoc-gui/unsafe-fn.goml | 17 +- tests/rustdoc-gui/warning-block.goml | 2 +- tests/rustdoc-gui/where-whitespace.goml | 10 +- .../const-in-super-trait-and-item-bound.rs | 23 + .../lifetime-generic-user-impl-normalize.rs | 17 + .../lifetime-generic-user-impl.rs | 11 + ...ctions-in-super-trait-bound-unsatisfied.rs | 18 + ...ns-in-super-trait-bound-unsatisfied.stderr | 25 + .../unconstrained-param-in-impl-ambiguity.rs | 10 + ...constrained-param-in-impl-ambiguity.stderr | 9 + tests/rustdoc/search-index-summaries.rs | 2 +- tests/rustdoc/synthetic_auto/bounds.rs | 21 + tests/rustdoc/synthetic_auto/complex.rs | 4 +- tests/rustdoc/synthetic_auto/lifetimes.rs | 2 +- tests/rustdoc/synthetic_auto/no-redundancy.rs | 5 +- tests/rustdoc/synthetic_auto/project.rs | 6 +- .../stable-mir/check_normalization.rs | 95 + tests/ui/abi/extern/extern-pass-FiveU16s.rs | 30 + tests/ui/abi/extern/extern-return-FiveU16s.rs | 26 + tests/ui/asm/x86_64/goto.rs | 2 - .../{goto.mirunsafeck.stderr => goto.stderr} | 4 +- tests/ui/asm/x86_64/goto.thirunsafeck.stderr | 23 - .../ui/async-await/async-closures/captures.rs | 116 + .../async-closures/captures.run.stdout | 14 + .../async-closures/wrong-fn-kind.rs | 10 +- .../async-closures/wrong-fn-kind.stderr | 49 +- tests/ui/async-await/async-is-unwindsafe.rs | 1 + .../ui/async-await/async-is-unwindsafe.stderr | 43 +- tests/ui/async-await/coroutine-desc.stderr | 28 +- .../auxiliary/assert-sigpipe-disposition.rs | 34 + .../unix_sigpipe-and-child-processes.rs | 56 + tests/ui/binop/binary-op-suggest-deref.stderr | 6 +- tests/ui/{issues => closures}/issue-1460.rs | 0 .../ui/{issues => closures}/issue-1460.stderr | 0 .../coerce-reborrow-multi-arg-fail.stderr | 10 +- .../regions-in-canonical.rs | 23 + .../regions-in-canonical.stderr | 11 + ...reporting-if-references-err.current.stderr | 27 + ...ip-reporting-if-references-err.next.stderr | 14 + .../skip-reporting-if-references-err.rs | 19 + .../ui/compiletest-self-test/test-aux-bin.rs | 2 +- .../generic_const_exprs/opaque_type.rs | 18 + .../generic_const_exprs/opaque_type.stderr | 22 + tests/ui/const-generics/opaque_types.rs | 13 + tests/ui/const-generics/opaque_types.stderr | 125 + tests/ui/const-generics/opaque_types2.rs | 17 + tests/ui/const-generics/opaque_types2.stderr | 15 + tests/ui/consts/const-eval/parse_ints.rs | 10 + tests/ui/consts/const-eval/parse_ints.stderr | 31 + ...const-extern-fn-requires-unsafe.mir.stderr | 19 - ...onst-extern-fn-requires-unsafe.thir.stderr | 19 - tests/ui/consts/const-int-unchecked.rs | 14 +- tests/ui/consts/const-int-unchecked.stderr | 24 +- tests/ui/derives/auxiliary/rustc-serialize.rs | 16 + .../derives/rustc-decodable-issue-123156.rs | 11 + .../rustc-decodable-issue-123156.stderr | 10 + tests/ui/does-nothing.rs | 2 - tests/ui/does-nothing.stderr | 9 - tests/ui/extern/issue-95829.stderr | 9 +- ....stderr => feature-gate-f128.e2015.stderr} | 10 +- .../feature-gate-f128.e2018.stderr | 53 + tests/ui/feature-gates/feature-gate-f128.rs | 4 + ...6.stderr => feature-gate-f16.e2015.stderr} | 10 +- .../feature-gate-f16.e2018.stderr | 53 + tests/ui/feature-gates/feature-gate-f16.rs | 4 + .../ui/feature-gates/feature-gate-mut-ref.rs | 13 + .../feature-gates/feature-gate-mut-ref.stderr | 43 + .../feature-gate-proc_macro_byte_character.rs | 10 - ...ture-gate-proc_macro_byte_character.stderr | 13 - .../feature-gate-proc_macro_c_str_literals.rs | 11 - ...ture-gate-proc_macro_c_str_literals.stderr | 13 - .../feature-gate-ref_pat_everywhere.rs | 14 + .../feature-gate-ref_pat_everywhere.stderr | 49 + tests/ui/fn/fn-item-lifetime-bounds.rs | 37 - tests/ui/fn/fn-item-type.stderr | 50 +- tests/ui/{issues => fn}/issue-1451.rs | 0 tests/ui/{issues => fn}/issue-1900.rs | 0 tests/ui/{issues => fn}/issue-1900.stderr | 0 ...mbig-hr-projection-issue-93340.next.stderr | 34 +- ...ambig-hr-projection-issue-93340.old.stderr | 2 +- .../ambig-hr-projection-issue-93340.rs | 4 +- .../builtin-closure-like-bounds.rs | 58 + .../closure-bound-codegen-ice.rs | 33 + .../candidate-from-env-universe-err-1.rs | 28 + .../candidate-from-env-universe-err-1.stderr | 26 + ...ate-from-env-universe-err-2.current.stderr | 25 + ...didate-from-env-universe-err-2.next.stderr | 19 + ...ndidate-from-env-universe-err-2.old.stderr | 26 + .../candidate-from-env-universe-err-2.rs | 20 + ...om-env-universe-err-project.current.stderr | 54 + ...-from-env-universe-err-project.next.stderr | 67 + ...candidate-from-env-universe-err-project.rs | 62 + .../leak-check-in-selection-1.rs} | 0 .../leak-check-in-selection-2.next.stderr | 23 + .../leak-check-in-selection-2.old.stderr | 23 + .../leak-check/leak-check-in-selection-2.rs | 18 + .../leak-check-in-selection-3.next.stderr | 35 + .../leak-check-in-selection-3.old.stderr | 48 + .../leak-check/leak-check-in-selection-3.rs | 39 + .../leak-check-in-selection-4-hr-nested.rs | 29 + .../trait-bounds/fn-ptr.classic.stderr | 19 - .../trait-bounds/fn-ptr.current.stderr | 19 - tests/ui/higher-ranked/trait-bounds/fn-ptr.rs | 3 +- .../trait-bounds/future.classic.stderr | 6 - .../trait-bounds/future.current.stderr | 6 - tests/ui/higher-ranked/trait-bounds/future.rs | 9 +- ...-30786.rs => hrtb-doesnt-borrow-self-1.rs} | 20 +- .../hrtb-doesnt-borrow-self-1.stderr | 27 + .../trait-bounds/hrtb-doesnt-borrow-self-2.rs | 116 + .../hrtb-doesnt-borrow-self-2.stderr | 27 + ...igher-ranker-supertraits-transitive.stderr | 22 +- .../hrtb-higher-ranker-supertraits.rs | 32 +- .../hrtb-higher-ranker-supertraits.stderr | 86 +- .../trait-bounds/issue-30786.stderr | 51 - .../in-trait/span-bug-issue-121457.rs | 1 + .../in-trait/span-bug-issue-121457.stderr | 19 +- .../in-trait/synthetic-hir-has-parent.rs | 11 + .../in-trait/synthetic-hir-has-parent.stderr | 27 + tests/ui/impl-trait/nested_impl_trait.rs | 4 +- tests/ui/impl-trait/nested_impl_trait.stderr | 8 +- .../recursive-coroutine-boxed.next.stderr | 28 +- .../impl-trait/recursive-coroutine-boxed.rs | 6 +- tests/ui/implied-bounds/issue-100690.rs | 11 +- tests/ui/implied-bounds/issue-100690.stderr | 49 +- tests/ui/issues/issue-1476.rs | 3 - tests/ui/issues/issue-1476.stderr | 9 - tests/ui/issues/issue-1696.rs | 8 - ...ice-non-last-unsized-field-issue-121473.rs | 79 + ...non-last-unsized-field-issue-121473.stderr | 106 + tests/ui/lifetimes/issue-105675.rs | 2 +- .../lifetimes/lifetime-errors/issue_74400.rs | 4 +- .../lint-strict-provenance-fuzzy-casts.stderr | 2 +- .../lint-strict-provenance-lossy-casts.stderr | 8 +- tests/ui/lint/wide_pointer_comparisons.rs | 36 + tests/ui/lint/wide_pointer_comparisons.stderr | 291 +- tests/ui/{issues => loops}/issue-1962.fixed | 0 tests/ui/{issues => loops}/issue-1962.rs | 0 tests/ui/{issues => loops}/issue-1962.stderr | 0 tests/ui/{issues => loops}/issue-1974.rs | 0 tests/ui/marker_trait_attr/unsound-overlap.rs | 1 + .../marker_trait_attr/unsound-overlap.stderr | 21 +- .../ui/match/postfix-match/match-after-as.rs | 7 + .../match/postfix-match/match-after-as.stderr | 28 + .../ref_pat_everywhere-mutability-mismatch.rs | 16 + ..._pat_everywhere-mutability-mismatch.stderr | 44 + tests/ui/match/ref_pat_everywhere.rs | 18 + tests/ui/methods/opaque_param_in_ufc.rs | 30 + tests/ui/methods/opaque_param_in_ufc.stderr | 36 + ...verflow-due-to-sized-predicate-ordering.rs | 30 + tests/ui/mir/alignment/packed.rs | 2 +- tests/ui/mir/const_eval_select_cycle.rs | 18 + tests/ui/mismatched_types/binops.stderr | 12 +- ...generic-mismatch-reporting-issue-116615.rs | 14 + ...ric-mismatch-reporting-issue-116615.stderr | 97 + .../issue-1362.rs | 0 .../issue-1362.stderr | 0 .../issue-1448-2.rs | 0 .../issue-1448-2.stderr | 0 tests/ui/mut/mut-ref.rs | 11 +- tests/ui/mut/mut-ref.stderr | 8 - tests/ui/nll/match-cfg-fake-edges.rs | 97 +- tests/ui/nll/match-cfg-fake-edges.stderr | 158 +- tests/ui/nll/match-cfg-fake-edges2.rs | 21 +- tests/ui/nll/match-cfg-fake-edges2.stderr | 4 +- tests/ui/parser/fn-header-semantic-fail.rs | 5 +- .../ui/parser/fn-header-semantic-fail.stderr | 68 +- .../ui/parser/no-const-fn-in-extern-block.rs | 1 + .../parser/no-const-fn-in-extern-block.stderr | 27 +- tests/ui/parser/unsafe-foreign-mod-2.stderr | 9 +- tests/ui/pattern/mut-ref-mut-2021.rs | 55 + tests/ui/pattern/mut-ref-mut-2021.stderr | 70 + tests/ui/pattern/usefulness/unions.rs | 35 + tests/ui/pattern/usefulness/unions.stderr | 34 + .../generic_struct_field_projection.rs | 38 + tests/ui/proc-macro/auxiliary/api/mod.rs | 2 - tests/ui/proc-macro/bad-projection.rs | 1 + tests/ui/proc-macro/bad-projection.stderr | 14 +- tests/ui/repr/repr-align.rs | 8 + tests/ui/repr/repr-align.stderr | 42 +- .../primitive-f16-f128-shadowed-mod.rs | 19 + .../ui/resolve/primitive-f16-f128-shadowed.rs | 3 + .../fn-ptr.mir.stderr | 23 - .../fn-ptr.thir.stderr | 23 - .../safe-calls.mir.stderr | 115 - .../safe-calls.thir.stderr | 115 - tests/ui/sanitizer/cfg.rs | 1 + tests/ui/sanitizer/cfi-async-closures.rs | 33 + tests/ui/sanitizer/cfi-closure-fn-ptr-cast.rs | 22 - tests/ui/sanitizer/cfi-closures.rs | 90 + tests/ui/sanitizer/cfi-complex-receiver.rs | 1 + tests/ui/sanitizer/cfi-coroutine.rs | 67 + tests/ui/sanitizer/cfi-method-fn-ptr-cast.rs | 57 + tests/ui/sanitizer/cfi-self-ref.rs | 1 + tests/ui/sanitizer/cfi-supertraits.rs | 73 + tests/ui/sanitizer/cfi-virtual-auto.rs | 1 + tests/ui/self/arbitrary-self-opaque.rs | 12 + tests/ui/self/arbitrary-self-opaque.stderr | 20 + tests/ui/simd/intrinsic/ptr-cast.rs | 10 +- ...dings-in-pattern-with-ty-err-doesnt-ice.rs | 7 + ...s-in-pattern-with-ty-err-doesnt-ice.stderr | 36 + tests/ui/specialization/issue-39448.rs | 3 +- tests/ui/specialization/issue-39448.stderr | 31 +- tests/ui/specialization/issue-39618.rs | 3 +- tests/ui/specialization/issue-39618.stderr | 20 +- .../min_specialization/impl-on-opaque.rs | 31 + .../min_specialization/impl-on-opaque2.rs | 28 + .../min_specialization/impl-on-opaque2.stderr | 12 + .../stability-in-private-module.rs | 4 +- .../stability-in-private-module.stderr | 16 +- tests/ui/{issues => static}/issue-1660.rs | 0 ...-allocations-dont-inherit-codegen-attrs.rs | 11 + tests/ui/statics/nested_thread_local.rs | 14 + tests/ui/statics/nested_thread_local.stderr | 8 + tests/ui/structs-enums/type-sizes.rs | 2 +- tests/ui/thir-print/thir-tree-match.stdout | 3 +- .../traits/stack-error-order-dependence-2.rs | 24 + .../ui/traits/stack-error-order-dependence.rs | 19 + ...gal-upcast-from-impl-opaque.current.stderr | 17 + ...llegal-upcast-from-impl-opaque.next.stderr | 14 + .../illegal-upcast-from-impl-opaque.rs | 29 + tests/ui/unpretty/hir-tree.rs | 10 + tests/ui/version-flags/version-info-flags.rs | 10 + tests/ui/wf/wf-fn-def-check-sig-1.rs | 44 + tests/ui/wf/wf-fn-def-check-sig-1.stderr | 15 + tests/ui/wf/wf-fn-def-check-sig-2.rs | 44 + tests/ui/wf/wf-fn-def-check-sig-2.stderr | 15 + triagebot.toml | 9 +- 1749 files changed, 27147 insertions(+), 16196 deletions(-) create mode 100644 compiler/rustc_codegen_cranelift/docs/rustc_testing.md delete mode 100644 compiler/rustc_middle/src/ty/binding.rs delete mode 100644 compiler/rustc_mir_transform/src/check_unsafety.rs rename compiler/rustc_mir_transform/src/{uninhabited_enum_branching.rs => unreachable_enum_branching.rs} (69%) delete mode 100644 compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs create mode 100644 library/alloc/src/lib.miri.rs create mode 100644 library/core/src/lib.miri.rs create mode 100644 library/std/src/lib.miri.rs create mode 100644 library/std/src/sys/pal/wasip2/cabi_realloc.rs delete mode 100644 library/std/src/sys_common/thread_info.rs create mode 100644 src/doc/unstable-book/src/compiler-flags/external-clangrt.md create mode 100644 src/librustdoc/html/render/search_index/encode.rs create mode 100644 src/tools/clippy/book/src/development/the_team.md create mode 100644 src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs create mode 100644 src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs create mode 100644 src/tools/clippy/tests/ui/crashes/ice-12616.fixed create mode 100644 src/tools/clippy/tests/ui/crashes/ice-12616.rs create mode 100644 src/tools/clippy/tests/ui/crashes/ice-12616.stderr create mode 100644 src/tools/clippy/tests/ui/legacy_numeric_constants.fixed create mode 100644 src/tools/clippy/tests/ui/legacy_numeric_constants.rs create mode 100644 src/tools/clippy/tests/ui/legacy_numeric_constants.stderr create mode 100644 src/tools/clippy/tests/ui/legacy_numeric_constants_unfixable.rs create mode 100644 src/tools/clippy/tests/ui/legacy_numeric_constants_unfixable.stderr create mode 100644 src/tools/clippy/tests/ui/manual_swap_auto_fix.fixed create mode 100644 src/tools/clippy/tests/ui/manual_swap_auto_fix.rs create mode 100644 src/tools/clippy/tests/ui/manual_swap_auto_fix.stderr create mode 100644 src/tools/clippy/tests/ui/missing_transmute_annotations.fixed create mode 100644 src/tools/clippy/tests/ui/missing_transmute_annotations.rs create mode 100644 src/tools/clippy/tests/ui/missing_transmute_annotations.stderr create mode 100644 src/tools/clippy/tests/ui/mixed_attributes_style/auxiliary/submodule.rs create mode 100644 src/tools/clippy/tests/ui/mixed_attributes_style/global_allow.rs create mode 100644 src/tools/clippy/tests/ui/mixed_attributes_style/mod_declaration.rs create mode 100644 src/tools/clippy/tests/ui/mixed_attributes_style/mod_declaration.stderr create mode 100644 src/tools/clippy/tests/ui/type_id_on_box_unfixable.rs create mode 100644 src/tools/clippy/tests/ui/type_id_on_box_unfixable.stderr delete mode 100644 src/tools/miri/test-cargo-miri/issue-1760/Cargo.toml create mode 100644 src/tools/miri/test-cargo-miri/proc-macro-crate/Cargo.toml rename src/tools/miri/test-cargo-miri/{issue-1760 => proc-macro-crate}/build.rs (100%) rename src/tools/miri/test-cargo-miri/{issue-1760 => proc-macro-crate}/src/lib.rs (100%) create mode 100644 src/tools/miri/tests/pass/async-closure-captures.rs create mode 100644 src/tools/miri/tests/pass/async-closure-captures.stdout create mode 100644 src/tools/run-make-support/src/cc.rs create mode 100644 src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_const.html create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rs rename src/tools/rust-analyzer/{crates/syntax/src/tests/sourcegen_ast.rs => xtask/src/codegen/grammar.rs} (93%) rename src/tools/rust-analyzer/{crates/syntax/src/tests => xtask/src/codegen/grammar}/ast_src.rs (100%) create mode 100644 src/tools/rustfmt/tests/source/mut_ref.rs create mode 100644 src/tools/rustfmt/tests/target/mut_ref.rs create mode 100644 tests/assembly/x86_64-cmp.rs create mode 100644 tests/codegen/cast-target-abi.rs delete mode 100644 tests/codegen/enum/uninhabited_enum_default_branch.rs create mode 100644 tests/codegen/enum/unreachable_enum_default_branch.rs create mode 100644 tests/codegen/intrinsics/three_way_compare.rs create mode 100644 tests/codegen/no-redundant-item-monomorphization.rs create mode 100644 tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-method-secondary-typeid.rs create mode 100644 tests/debuginfo/path.rs rename tests/mir-opt/{ => building/match}/deref-patterns/string.foo.PreCodegen.after.mir (100%) rename tests/mir-opt/{ => building/match}/deref-patterns/string.rs (100%) rename tests/mir-opt/{ => building/match}/exponential_or.match_tuple.SimplifyCfg-initial.after.mir (100%) rename tests/mir-opt/{ => building/match}/exponential_or.rs (100%) rename tests/mir-opt/building/{ => match}/match_false_edges.full_tested_match.built.after.mir (100%) rename tests/mir-opt/building/{ => match}/match_false_edges.full_tested_match2.built.after.mir (100%) rename tests/mir-opt/building/{ => match}/match_false_edges.main.built.after.mir (98%) rename tests/mir-opt/building/{ => match}/match_false_edges.rs (100%) rename tests/mir-opt/building/{ => match}/simple_match.match_bool.built.after.mir (100%) rename tests/mir-opt/building/{ => match}/simple_match.rs (100%) create mode 100644 tests/mir-opt/building/match/sort_candidates.constant_eq.SimplifyCfg-initial.after.mir create mode 100644 tests/mir-opt/building/match/sort_candidates.disjoint_ranges.SimplifyCfg-initial.after.mir create mode 100644 tests/mir-opt/building/match/sort_candidates.rs rename tests/mir-opt/const_prop/{pointer_expose_address.main.GVN.panic-abort.diff => pointer_expose_provenance.main.GVN.panic-abort.diff} (93%) rename tests/mir-opt/const_prop/{pointer_expose_address.main.GVN.panic-unwind.diff => pointer_expose_provenance.main.GVN.panic-unwind.diff} (93%) rename tests/mir-opt/const_prop/{pointer_expose_address.rs => pointer_expose_provenance.rs} (72%) delete mode 100644 tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.Inline.panic-abort.diff delete mode 100644 tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.Inline.panic-unwind.diff delete mode 100644 tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.PreCodegen.after.panic-abort.mir delete mode 100644 tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_bigger.PreCodegen.after.panic-unwind.mir delete mode 100644 tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-abort.diff delete mode 100644 tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-unwind.diff delete mode 100644 tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-abort.mir delete mode 100644 tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-unwind.mir create mode 100644 tests/mir-opt/instsimplify/ub_check.rs create mode 100644 tests/mir-opt/instsimplify/ub_check.unwrap_unchecked.InstSimplify.diff create mode 100644 tests/mir-opt/lower_intrinsics.three_way_compare_char.LowerIntrinsics.panic-abort.diff create mode 100644 tests/mir-opt/lower_intrinsics.three_way_compare_char.LowerIntrinsics.panic-unwind.diff create mode 100644 tests/mir-opt/lower_intrinsics.three_way_compare_signed.LowerIntrinsics.panic-abort.diff create mode 100644 tests/mir-opt/lower_intrinsics.three_way_compare_signed.LowerIntrinsics.panic-unwind.diff create mode 100644 tests/mir-opt/lower_intrinsics.three_way_compare_unsigned.LowerIntrinsics.panic-abort.diff create mode 100644 tests/mir-opt/lower_intrinsics.three_way_compare_unsigned.LowerIntrinsics.panic-unwind.diff delete mode 100644 tests/mir-opt/match_test.main.SimplifyCfg-initial.after.mir delete mode 100644 tests/mir-opt/match_test.rs create mode 100644 tests/mir-opt/pre-codegen/derived_ord.rs create mode 100644 tests/mir-opt/pre-codegen/derived_ord.{impl#0}-partial_cmp.PreCodegen.after.mir create mode 100644 tests/mir-opt/simplify_dead_blocks.assert_nonzero_nonmax.SimplifyCfg-after-unreachable-enum-branching.diff create mode 100644 tests/mir-opt/simplify_dead_blocks.rs delete mode 100644 tests/mir-opt/simplify_duplicate_unreachable_blocks.assert_nonzero_nonmax.SimplifyCfg-after-uninhabited-enum-branching.diff delete mode 100644 tests/mir-opt/simplify_duplicate_unreachable_blocks.rs rename tests/mir-opt/{simplify_locals.expose_addr.SimplifyLocals-before-const-prop.diff => simplify_locals.expose_provenance.SimplifyLocals-before-const-prop.diff} (54%) rename tests/mir-opt/{uninhabited_fallthrough_elimination.eliminate_fallthrough.UninhabitedEnumBranching.diff => uninhabited_fallthrough_elimination.eliminate_fallthrough.UnreachableEnumBranching.diff} (83%) rename tests/mir-opt/{uninhabited_fallthrough_elimination.keep_fallthrough.UninhabitedEnumBranching.diff => uninhabited_fallthrough_elimination.keep_fallthrough.UnreachableEnumBranching.diff} (83%) rename tests/mir-opt/{uninhabited_enum_branching.byref.UninhabitedEnumBranching.panic-abort.diff => unreachable_enum_branching.byref.UnreachableEnumBranching.panic-abort.diff} (95%) rename tests/mir-opt/{uninhabited_enum_branching.byref.UninhabitedEnumBranching.panic-unwind.diff => unreachable_enum_branching.byref.UnreachableEnumBranching.panic-unwind.diff} (95%) rename tests/mir-opt/{uninhabited_enum_branching.custom_discriminant.UninhabitedEnumBranching.panic-abort.diff => unreachable_enum_branching.custom_discriminant.UnreachableEnumBranching.panic-abort.diff} (85%) rename tests/mir-opt/{uninhabited_enum_branching.custom_discriminant.UninhabitedEnumBranching.panic-unwind.diff => unreachable_enum_branching.custom_discriminant.UnreachableEnumBranching.panic-unwind.diff} (85%) rename tests/mir-opt/{uninhabited_enum_branching.otherwise_t1.UninhabitedEnumBranching.panic-abort.diff => unreachable_enum_branching.otherwise_t1.UnreachableEnumBranching.panic-abort.diff} (89%) rename tests/mir-opt/{uninhabited_enum_branching.otherwise_t1.UninhabitedEnumBranching.panic-unwind.diff => unreachable_enum_branching.otherwise_t1.UnreachableEnumBranching.panic-unwind.diff} (89%) rename tests/mir-opt/{uninhabited_enum_branching.otherwise_t2.UninhabitedEnumBranching.panic-abort.diff => unreachable_enum_branching.otherwise_t2.UnreachableEnumBranching.panic-abort.diff} (87%) rename tests/mir-opt/{uninhabited_enum_branching.otherwise_t2.UninhabitedEnumBranching.panic-unwind.diff => unreachable_enum_branching.otherwise_t2.UnreachableEnumBranching.panic-unwind.diff} (87%) rename tests/mir-opt/{uninhabited_enum_branching.otherwise_t3.UninhabitedEnumBranching.panic-abort.diff => unreachable_enum_branching.otherwise_t3.UnreachableEnumBranching.panic-abort.diff} (89%) rename tests/mir-opt/{uninhabited_enum_branching.otherwise_t3.UninhabitedEnumBranching.panic-unwind.diff => unreachable_enum_branching.otherwise_t3.UnreachableEnumBranching.panic-unwind.diff} (89%) rename tests/mir-opt/{uninhabited_enum_branching.otherwise_t4.UninhabitedEnumBranching.panic-abort.diff => unreachable_enum_branching.otherwise_t4.UnreachableEnumBranching.panic-abort.diff} (88%) rename tests/mir-opt/{uninhabited_enum_branching.otherwise_t4.UninhabitedEnumBranching.panic-unwind.diff => unreachable_enum_branching.otherwise_t4.UnreachableEnumBranching.panic-unwind.diff} (88%) rename tests/mir-opt/{uninhabited_enum_branching.otherwise_t4_uninhabited_default.UninhabitedEnumBranching.panic-abort.diff => unreachable_enum_branching.otherwise_t4_unreachable_default.UnreachableEnumBranching.panic-abort.diff} (84%) rename tests/mir-opt/{uninhabited_enum_branching.otherwise_t4_uninhabited_default.UninhabitedEnumBranching.panic-unwind.diff => unreachable_enum_branching.otherwise_t4_unreachable_default.UnreachableEnumBranching.panic-unwind.diff} (84%) rename tests/mir-opt/{uninhabited_enum_branching.otherwise_t4_uninhabited_default_2.UninhabitedEnumBranching.panic-abort.diff => unreachable_enum_branching.otherwise_t4_unreachable_default_2.UnreachableEnumBranching.panic-abort.diff} (87%) rename tests/mir-opt/{uninhabited_enum_branching.otherwise_t4_uninhabited_default_2.UninhabitedEnumBranching.panic-unwind.diff => unreachable_enum_branching.otherwise_t4_unreachable_default_2.UnreachableEnumBranching.panic-unwind.diff} (87%) rename tests/mir-opt/{uninhabited_enum_branching.otherwise_t5_uninhabited_default.UninhabitedEnumBranching.panic-abort.diff => unreachable_enum_branching.otherwise_t5_unreachable_default.UnreachableEnumBranching.panic-abort.diff} (85%) rename tests/mir-opt/{uninhabited_enum_branching.otherwise_t5_uninhabited_default.UninhabitedEnumBranching.panic-unwind.diff => unreachable_enum_branching.otherwise_t5_unreachable_default.UnreachableEnumBranching.panic-unwind.diff} (86%) rename tests/mir-opt/{uninhabited_enum_branching.rs => unreachable_enum_branching.rs} (80%) rename tests/mir-opt/{uninhabited_enum_branching.simple.UninhabitedEnumBranching.panic-abort.diff => unreachable_enum_branching.simple.UnreachableEnumBranching.panic-abort.diff} (90%) rename tests/mir-opt/{uninhabited_enum_branching.simple.UninhabitedEnumBranching.panic-unwind.diff => unreachable_enum_branching.simple.UnreachableEnumBranching.panic-unwind.diff} (90%) delete mode 100644 tests/run-make/arguments-non-c-like-enum/Makefile create mode 100644 tests/run-make/arguments-non-c-like-enum/rmake.rs delete mode 100644 tests/run-make/hir-tree/Makefile delete mode 100644 tests/run-make/hir-tree/input.rs delete mode 100644 tests/run-make/issue-7349/Makefile delete mode 100644 tests/run-make/issue-7349/foo.rs create mode 100644 tests/run-make/non-unicode-env/non_unicode_env.rs create mode 100644 tests/run-make/non-unicode-env/non_unicode_env.stderr create mode 100644 tests/run-make/non-unicode-env/rmake.rs delete mode 100644 tests/run-make/version/Makefile create mode 100644 tests/rustdoc-ui/synthetic-auto-trait-impls/const-in-super-trait-and-item-bound.rs create mode 100644 tests/rustdoc-ui/synthetic-auto-trait-impls/lifetime-generic-user-impl-normalize.rs create mode 100644 tests/rustdoc-ui/synthetic-auto-trait-impls/lifetime-generic-user-impl.rs create mode 100644 tests/rustdoc-ui/synthetic-auto-trait-impls/projections-in-super-trait-bound-unsatisfied.rs create mode 100644 tests/rustdoc-ui/synthetic-auto-trait-impls/projections-in-super-trait-bound-unsatisfied.stderr create mode 100644 tests/rustdoc-ui/synthetic-auto-trait-impls/unconstrained-param-in-impl-ambiguity.rs create mode 100644 tests/rustdoc-ui/synthetic-auto-trait-impls/unconstrained-param-in-impl-ambiguity.stderr create mode 100644 tests/rustdoc/synthetic_auto/bounds.rs create mode 100644 tests/ui-fulldeps/stable-mir/check_normalization.rs create mode 100644 tests/ui/abi/extern/extern-pass-FiveU16s.rs create mode 100644 tests/ui/abi/extern/extern-return-FiveU16s.rs rename tests/ui/asm/x86_64/{goto.mirunsafeck.stderr => goto.stderr} (92%) delete mode 100644 tests/ui/asm/x86_64/goto.thirunsafeck.stderr create mode 100644 tests/ui/async-await/async-closures/captures.rs create mode 100644 tests/ui/async-await/async-closures/captures.run.stdout create mode 100644 tests/ui/attributes/unix_sigpipe/auxiliary/assert-sigpipe-disposition.rs create mode 100644 tests/ui/attributes/unix_sigpipe/unix_sigpipe-and-child-processes.rs rename tests/ui/{issues => closures}/issue-1460.rs (100%) rename tests/ui/{issues => closures}/issue-1460.stderr (100%) create mode 100644 tests/ui/coherence/negative-coherence/regions-in-canonical.rs create mode 100644 tests/ui/coherence/negative-coherence/regions-in-canonical.stderr create mode 100644 tests/ui/coherence/skip-reporting-if-references-err.current.stderr create mode 100644 tests/ui/coherence/skip-reporting-if-references-err.next.stderr create mode 100644 tests/ui/coherence/skip-reporting-if-references-err.rs create mode 100644 tests/ui/const-generics/generic_const_exprs/opaque_type.rs create mode 100644 tests/ui/const-generics/generic_const_exprs/opaque_type.stderr create mode 100644 tests/ui/const-generics/opaque_types.rs create mode 100644 tests/ui/const-generics/opaque_types.stderr create mode 100644 tests/ui/const-generics/opaque_types2.rs create mode 100644 tests/ui/const-generics/opaque_types2.stderr create mode 100644 tests/ui/consts/const-eval/parse_ints.rs create mode 100644 tests/ui/consts/const-eval/parse_ints.stderr delete mode 100644 tests/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.mir.stderr delete mode 100644 tests/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.thir.stderr create mode 100644 tests/ui/derives/auxiliary/rustc-serialize.rs create mode 100644 tests/ui/derives/rustc-decodable-issue-123156.rs create mode 100644 tests/ui/derives/rustc-decodable-issue-123156.stderr delete mode 100644 tests/ui/does-nothing.rs delete mode 100644 tests/ui/does-nothing.stderr rename tests/ui/feature-gates/{feature-gate-f128.stderr => feature-gate-f128.e2015.stderr} (91%) create mode 100644 tests/ui/feature-gates/feature-gate-f128.e2018.stderr rename tests/ui/feature-gates/{feature-gate-f16.stderr => feature-gate-f16.e2015.stderr} (91%) create mode 100644 tests/ui/feature-gates/feature-gate-f16.e2018.stderr create mode 100644 tests/ui/feature-gates/feature-gate-mut-ref.rs create mode 100644 tests/ui/feature-gates/feature-gate-mut-ref.stderr delete mode 100644 tests/ui/feature-gates/feature-gate-proc_macro_byte_character.rs delete mode 100644 tests/ui/feature-gates/feature-gate-proc_macro_byte_character.stderr delete mode 100644 tests/ui/feature-gates/feature-gate-proc_macro_c_str_literals.rs delete mode 100644 tests/ui/feature-gates/feature-gate-proc_macro_c_str_literals.stderr create mode 100644 tests/ui/feature-gates/feature-gate-ref_pat_everywhere.rs create mode 100644 tests/ui/feature-gates/feature-gate-ref_pat_everywhere.stderr delete mode 100644 tests/ui/fn/fn-item-lifetime-bounds.rs rename tests/ui/{issues => fn}/issue-1451.rs (100%) rename tests/ui/{issues => fn}/issue-1900.rs (100%) rename tests/ui/{issues => fn}/issue-1900.stderr (100%) create mode 100644 tests/ui/higher-ranked/builtin-closure-like-bounds.rs create mode 100644 tests/ui/higher-ranked/closure-bound-codegen-ice.rs create mode 100644 tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-1.rs create mode 100644 tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-1.stderr create mode 100644 tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-2.current.stderr create mode 100644 tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-2.next.stderr create mode 100644 tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-2.old.stderr create mode 100644 tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-2.rs create mode 100644 tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.current.stderr create mode 100644 tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.next.stderr create mode 100644 tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.rs rename tests/ui/higher-ranked/{leak-check-in-selection.rs => leak-check/leak-check-in-selection-1.rs} (100%) create mode 100644 tests/ui/higher-ranked/leak-check/leak-check-in-selection-2.next.stderr create mode 100644 tests/ui/higher-ranked/leak-check/leak-check-in-selection-2.old.stderr create mode 100644 tests/ui/higher-ranked/leak-check/leak-check-in-selection-2.rs create mode 100644 tests/ui/higher-ranked/leak-check/leak-check-in-selection-3.next.stderr create mode 100644 tests/ui/higher-ranked/leak-check/leak-check-in-selection-3.old.stderr create mode 100644 tests/ui/higher-ranked/leak-check/leak-check-in-selection-3.rs create mode 100644 tests/ui/higher-ranked/leak-check/leak-check-in-selection-4-hr-nested.rs delete mode 100644 tests/ui/higher-ranked/trait-bounds/fn-ptr.classic.stderr delete mode 100644 tests/ui/higher-ranked/trait-bounds/fn-ptr.current.stderr delete mode 100644 tests/ui/higher-ranked/trait-bounds/future.classic.stderr delete mode 100644 tests/ui/higher-ranked/trait-bounds/future.current.stderr rename tests/ui/higher-ranked/trait-bounds/{issue-30786.rs => hrtb-doesnt-borrow-self-1.rs} (86%) create mode 100644 tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-1.stderr create mode 100644 tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-2.rs create mode 100644 tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-2.stderr delete mode 100644 tests/ui/higher-ranked/trait-bounds/issue-30786.stderr create mode 100644 tests/ui/impl-trait/in-trait/synthetic-hir-has-parent.rs create mode 100644 tests/ui/impl-trait/in-trait/synthetic-hir-has-parent.stderr delete mode 100644 tests/ui/issues/issue-1476.rs delete mode 100644 tests/ui/issues/issue-1476.stderr delete mode 100644 tests/ui/issues/issue-1696.rs create mode 100644 tests/ui/layout/ice-non-last-unsized-field-issue-121473.rs create mode 100644 tests/ui/layout/ice-non-last-unsized-field-issue-121473.stderr rename tests/ui/{issues => loops}/issue-1962.fixed (100%) rename tests/ui/{issues => loops}/issue-1962.rs (100%) rename tests/ui/{issues => loops}/issue-1962.stderr (100%) rename tests/ui/{issues => loops}/issue-1974.rs (100%) create mode 100644 tests/ui/match/postfix-match/match-after-as.rs create mode 100644 tests/ui/match/postfix-match/match-after-as.stderr create mode 100644 tests/ui/match/ref_pat_everywhere-mutability-mismatch.rs create mode 100644 tests/ui/match/ref_pat_everywhere-mutability-mismatch.stderr create mode 100644 tests/ui/match/ref_pat_everywhere.rs create mode 100644 tests/ui/methods/opaque_param_in_ufc.rs create mode 100644 tests/ui/methods/opaque_param_in_ufc.stderr create mode 100644 tests/ui/methods/probe-overflow-due-to-sized-predicate-ordering.rs create mode 100644 tests/ui/mir/const_eval_select_cycle.rs create mode 100644 tests/ui/mismatched_types/generic-mismatch-reporting-issue-116615.rs create mode 100644 tests/ui/mismatched_types/generic-mismatch-reporting-issue-116615.stderr rename tests/ui/{issues => mismatched_types}/issue-1362.rs (100%) rename tests/ui/{issues => mismatched_types}/issue-1362.stderr (100%) rename tests/ui/{issues => mismatched_types}/issue-1448-2.rs (100%) rename tests/ui/{issues => mismatched_types}/issue-1448-2.stderr (100%) delete mode 100644 tests/ui/mut/mut-ref.stderr create mode 100644 tests/ui/pattern/mut-ref-mut-2021.rs create mode 100644 tests/ui/pattern/mut-ref-mut-2021.stderr create mode 100644 tests/ui/pattern/usefulness/unions.rs create mode 100644 tests/ui/pattern/usefulness/unions.stderr create mode 100644 tests/ui/privacy/generic_struct_field_projection.rs create mode 100644 tests/ui/resolve/primitive-f16-f128-shadowed-mod.rs delete mode 100644 tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.mir.stderr delete mode 100644 tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.thir.stderr delete mode 100644 tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.mir.stderr delete mode 100644 tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.thir.stderr create mode 100644 tests/ui/sanitizer/cfi-async-closures.rs delete mode 100644 tests/ui/sanitizer/cfi-closure-fn-ptr-cast.rs create mode 100644 tests/ui/sanitizer/cfi-closures.rs create mode 100644 tests/ui/sanitizer/cfi-coroutine.rs create mode 100644 tests/ui/sanitizer/cfi-method-fn-ptr-cast.rs create mode 100644 tests/ui/sanitizer/cfi-supertraits.rs create mode 100644 tests/ui/self/arbitrary-self-opaque.rs create mode 100644 tests/ui/self/arbitrary-self-opaque.stderr create mode 100644 tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs create mode 100644 tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr create mode 100644 tests/ui/specialization/min_specialization/impl-on-opaque.rs create mode 100644 tests/ui/specialization/min_specialization/impl-on-opaque2.rs create mode 100644 tests/ui/specialization/min_specialization/impl-on-opaque2.stderr rename tests/ui/{issues => static}/issue-1660.rs (100%) create mode 100644 tests/ui/statics/nested-allocations-dont-inherit-codegen-attrs.rs create mode 100644 tests/ui/statics/nested_thread_local.rs create mode 100644 tests/ui/statics/nested_thread_local.stderr create mode 100644 tests/ui/traits/stack-error-order-dependence-2.rs create mode 100644 tests/ui/traits/stack-error-order-dependence.rs create mode 100644 tests/ui/traits/trait-upcasting/illegal-upcast-from-impl-opaque.current.stderr create mode 100644 tests/ui/traits/trait-upcasting/illegal-upcast-from-impl-opaque.next.stderr create mode 100644 tests/ui/traits/trait-upcasting/illegal-upcast-from-impl-opaque.rs create mode 100644 tests/ui/unpretty/hir-tree.rs create mode 100644 tests/ui/version-flags/version-info-flags.rs create mode 100644 tests/ui/wf/wf-fn-def-check-sig-1.rs create mode 100644 tests/ui/wf/wf-fn-def-check-sig-1.stderr create mode 100644 tests/ui/wf/wf-fn-def-check-sig-2.rs create mode 100644 tests/ui/wf/wf-fn-def-check-sig-2.stderr diff --git a/Cargo.lock b/Cargo.lock index c4501d6e574f0..bf31bf72d4c18 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -593,7 +593,6 @@ dependencies = [ "syn 2.0.55", "tempfile", "termize", - "tester", "tokio", "toml 0.7.8", "ui_test 0.22.2", @@ -2590,6 +2589,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "nu-ansi-term" +version = "0.49.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c073d3c1930d0751774acf49e66653acecb416c3a54c6ec095a9b11caddb5a68" +dependencies = [ + "windows-sys 0.48.0", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -3560,6 +3568,7 @@ dependencies = [ name = "rustc_attr" version = "0.0.0" dependencies = [ + "rustc_abi", "rustc_ast", "rustc_ast_pretty", "rustc_data_structures", @@ -4773,6 +4782,8 @@ version = "0.0.0" dependencies = [ "arrayvec", "askama", + "base64", + "byteorder", "expect-test", "indexmap", "itertools 0.12.1", @@ -5358,9 +5369,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.30.7" +version = "0.30.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c385888ef380a852a16209afc8cfad22795dd8873d69c9a14d2e2088f118d18" +checksum = "4b1a378e48fb3ce3a5cf04359c456c9c98ff689bcf1c1bc6e6a31f247686f275" dependencies = [ "cfg-if", "core-foundation-sys", @@ -5496,19 +5507,6 @@ dependencies = [ "std", ] -[[package]] -name = "tester" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e8bf7e0eb2dd7b4228cc1b6821fc5114cd6841ae59f652a85488c016091e5f" -dependencies = [ - "cfg-if", - "getopts", - "libc", - "num_cpus", - "term", -] - [[package]] name = "thin-vec" version = "0.2.13" @@ -5778,17 +5776,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "tracing-log" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - [[package]] name = "tracing-log" version = "0.2.0" @@ -5807,7 +5794,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ "matchers", - "nu-ansi-term", + "nu-ansi-term 0.46.0", "once_cell", "parking_lot", "regex", @@ -5816,18 +5803,18 @@ dependencies = [ "thread_local", "tracing", "tracing-core", - "tracing-log 0.2.0", + "tracing-log", ] [[package]] name = "tracing-tree" -version = "0.2.5" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ec6adcab41b1391b08a308cc6302b79f8095d1673f6947c2dc65ffb028b0b2d" +checksum = "65139ecd2c3f6484c3b99bc01c77afe21e95473630747c7aca525e78b0666675" dependencies = [ - "nu-ansi-term", + "nu-ansi-term 0.49.0", "tracing-core", - "tracing-log 0.1.4", + "tracing-log", "tracing-subscriber", ] diff --git a/README.md b/README.md index 6d6383351caf7..3690a9c93c528 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,36 @@ -# The Rust Programming Language - -[![Rust Community](https://img.shields.io/badge/Rust_Community%20-Join_us-brightgreen?style=plastic&logo=rust)](https://www.rust-lang.org/community) +
+ + + + The Rust Programming Language: A language empowering everyone to build reliable and efficient software + + +[Website][Rust] | [Getting started] | [Learn] | [Documentation] | [Contributing] +
This is the main source code repository for [Rust]. It contains the compiler, standard library, and documentation. [Rust]: https://www.rust-lang.org/ +[Getting Started]: https://www.rust-lang.org/learn/get-started +[Learn]: https://www.rust-lang.org/learn +[Documentation]: https://www.rust-lang.org/learn#learn-use +[Contributing]: CONTRIBUTING.md + +## Why Rust? -**Note: this README is for _users_ rather than _contributors_.** -If you wish to _contribute_ to the compiler, you should read -[CONTRIBUTING.md](CONTRIBUTING.md) instead. +- **Performance:** Fast and memory-efficient, suitable for critical services, embedded devices, and easily integrate with other languages. -
-Table of Contents +- **Reliability:** Our rich type system and ownership model ensure memory and thread safety, reducing bugs at compile-time. -- [Quick Start](#quick-start) -- [Installing from Source](#installing-from-source) -- [Getting Help](#getting-help) -- [Contributing](#contributing) -- [License](#license) -- [Trademark](#trademark) +- **Productivity:** Comprehensive documentation, a compiler committed to providing great diagnostics, and advanced tooling including package manager and build tool ([Cargo]), auto-formatter ([rustfmt]), linter ([Clippy]) and editor support ([rust-analyzer]). -
+[Cargo]: https://github.com/rust-lang/cargo +[rustfmt]: https://github.com/rust-lang/rustfmt +[Clippy]: https://github.com/rust-lang/rust-clippy +[rust-analyzer]: https://github.com/rust-lang/rust-analyzer ## Quick Start diff --git a/RELEASES.md b/RELEASES.md index 922afdd3e1852..f35dd27ec2475 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,13 @@ +Version 1.77.1 (2024-03-28) +=========================== + + + +- [Revert stripping debuginfo by default for Windows](https://github.com/rust-lang/cargo/pull/13654) + This fixes a regression in 1.77 by reverting to the previous default. + Platforms other than Windows are not affected. +- Internal: [Fix heading anchor rendering in doc pages](https://github.com/rust-lang/rust/pull/122693) + Version 1.77.0 (2024-03-21) ========================== diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 4f2b9d0ef50d9..7439af7aed3ea 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -698,6 +698,7 @@ impl fmt::Display for AlignFromBytesError { impl Align { pub const ONE: Align = Align { pow2: 0 }; + pub const EIGHT: Align = Align { pow2: 3 }; // LLVM has a maximal supported alignment of 2^29, we inherit that. pub const MAX: Align = Align { pow2: 29 }; @@ -707,19 +708,19 @@ impl Align { } #[inline] - pub fn from_bytes(align: u64) -> Result { + pub const fn from_bytes(align: u64) -> Result { // Treat an alignment of 0 bytes like 1-byte alignment. if align == 0 { return Ok(Align::ONE); } #[cold] - fn not_power_of_2(align: u64) -> AlignFromBytesError { + const fn not_power_of_2(align: u64) -> AlignFromBytesError { AlignFromBytesError::NotPowerOfTwo(align) } #[cold] - fn too_large(align: u64) -> AlignFromBytesError { + const fn too_large(align: u64) -> AlignFromBytesError { AlignFromBytesError::TooLarge(align) } diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 915cb386075d9..e29ef591bcb5c 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -702,19 +702,10 @@ pub struct PatField { #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Encodable, Decodable, HashStable_Generic)] pub enum ByRef { - Yes, + Yes(Mutability), No, } -impl From for ByRef { - fn from(b: bool) -> ByRef { - match b { - false => ByRef::No, - true => ByRef::Yes, - } - } -} - /// Explicit binding annotations given in the HIR for a binding. Note /// that this is not the final binding *mode* that we infer after type /// inference. @@ -724,9 +715,11 @@ pub struct BindingAnnotation(pub ByRef, pub Mutability); impl BindingAnnotation { pub const NONE: Self = Self(ByRef::No, Mutability::Not); - pub const REF: Self = Self(ByRef::Yes, Mutability::Not); + pub const REF: Self = Self(ByRef::Yes(Mutability::Not), Mutability::Not); pub const MUT: Self = Self(ByRef::No, Mutability::Mut); - pub const REF_MUT: Self = Self(ByRef::Yes, Mutability::Mut); + pub const REF_MUT: Self = Self(ByRef::Yes(Mutability::Mut), Mutability::Not); + pub const MUT_REF: Self = Self(ByRef::Yes(Mutability::Not), Mutability::Mut); + pub const MUT_REF_MUT: Self = Self(ByRef::Yes(Mutability::Mut), Mutability::Mut); pub fn prefix_str(self) -> &'static str { match self { @@ -734,6 +727,8 @@ impl BindingAnnotation { Self::REF => "ref ", Self::MUT => "mut ", Self::REF_MUT => "ref mut ", + Self::MUT_REF => "mut ref ", + Self::MUT_REF_MUT => "mut ref mut ", } } } @@ -1281,7 +1276,8 @@ impl Expr { ExprKind::While(..) => ExprPrecedence::While, ExprKind::ForLoop { .. } => ExprPrecedence::ForLoop, ExprKind::Loop(..) => ExprPrecedence::Loop, - ExprKind::Match(..) => ExprPrecedence::Match, + ExprKind::Match(_, _, MatchKind::Prefix) => ExprPrecedence::Match, + ExprKind::Match(_, _, MatchKind::Postfix) => ExprPrecedence::PostfixMatch, ExprKind::Closure(..) => ExprPrecedence::Closure, ExprKind::Block(..) => ExprPrecedence::Block, ExprKind::TryBlock(..) => ExprPrecedence::TryBlock, @@ -2488,6 +2484,14 @@ pub enum CoroutineKind { } impl CoroutineKind { + pub fn span(self) -> Span { + match self { + CoroutineKind::Async { span, .. } => span, + CoroutineKind::Gen { span, .. } => span, + CoroutineKind::AsyncGen { span, .. } => span, + } + } + pub fn is_async(self) -> bool { matches!(self, CoroutineKind::Async { .. }) } @@ -3346,7 +3350,7 @@ impl TryFrom for ForeignItemKind { pub type ForeignItem = Item; // Some nodes are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; diff --git a/compiler/rustc_ast/src/ptr.rs b/compiler/rustc_ast/src/ptr.rs index 0140fb752bf92..e22a523dbc3ef 100644 --- a/compiler/rustc_ast/src/ptr.rs +++ b/compiler/rustc_ast/src/ptr.rs @@ -1,6 +1,6 @@ //! The AST pointer. //! -//! Provides `P`, a frozen owned smart pointer. +//! Provides [`P`][struct@P], an owned smart pointer. //! //! # Motivations and benefits //! @@ -8,18 +8,14 @@ //! passes (e.g., one may be able to bypass the borrow checker with a shared //! `ExprKind::AddrOf` node taking a mutable borrow). //! -//! * **Immutability**: `P` disallows mutating its inner `T`, unlike `Box` -//! (unless it contains an `Unsafe` interior, but that may be denied later). -//! This mainly prevents mistakes, but also enforces a kind of "purity". -//! //! * **Efficiency**: folding can reuse allocation space for `P` and `Vec`, //! the latter even when the input and output types differ (as it would be the //! case with arenas or a GADT AST using type parameters to toggle features). //! -//! * **Maintainability**: `P` provides a fixed interface - `Deref`, -//! `and_then` and `map` - which can remain fully functional even if the -//! implementation changes (using a special thread-local heap, for example). -//! Moreover, a switch to, e.g., `P<'a, T>` would be easy and mostly automated. +//! * **Maintainability**: `P` provides an interface, which can remain fully +//! functional even if the implementation changes (using a special thread-local +//! heap, for example). Moreover, a switch to, e.g., `P<'a, T>` would be easy +//! and mostly automated. use std::fmt::{self, Debug, Display}; use std::ops::{Deref, DerefMut}; @@ -29,6 +25,8 @@ use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; /// An owned smart pointer. +/// +/// See the [module level documentation][crate::ptr] for details. pub struct P { ptr: Box, } diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index f49eb2f22c50b..5060bbec42169 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -1021,7 +1021,7 @@ where } // Some types are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 239735456ad53..f3249f3e5a8b5 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -768,7 +768,7 @@ impl DelimSpacing { } // Some types are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs index 13768c1201791..373c0ebcc5cba 100644 --- a/compiler/rustc_ast/src/util/parser.rs +++ b/compiler/rustc_ast/src/util/parser.rs @@ -281,6 +281,7 @@ pub enum ExprPrecedence { ForLoop, Loop, Match, + PostfixMatch, ConstBlock, Block, TryBlock, @@ -334,7 +335,8 @@ impl ExprPrecedence { | ExprPrecedence::InlineAsm | ExprPrecedence::Mac | ExprPrecedence::FormatArgs - | ExprPrecedence::OffsetOf => PREC_POSTFIX, + | ExprPrecedence::OffsetOf + | ExprPrecedence::PostfixMatch => PREC_POSTFIX, // Never need parens ExprPrecedence::Array @@ -390,7 +392,8 @@ pub fn contains_exterior_struct_lit(value: &ast::Expr) -> bool { | ast::ExprKind::Cast(x, _) | ast::ExprKind::Type(x, _) | ast::ExprKind::Field(x, _) - | ast::ExprKind::Index(x, _, _) => { + | ast::ExprKind::Index(x, _, _) + | ast::ExprKind::Match(x, _, ast::MatchKind::Postfix) => { // &X { y: 1 }, X { y: 1 }.y contains_exterior_struct_lit(x) } diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index a1164008d0daf..1c34fd0afbb65 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -3,7 +3,7 @@ use rustc_hir as hir; use rustc_hir::def_id::{LocalDefId, LocalDefIdMap}; use rustc_hir::intravisit::Visitor; use rustc_hir::*; -use rustc_index::{Idx, IndexVec}; +use rustc_index::IndexVec; use rustc_middle::span_bug; use rustc_middle::ty::TyCtxt; use rustc_span::{Span, DUMMY_SP}; @@ -19,7 +19,7 @@ struct NodeCollector<'a, 'hir> { parenting: LocalDefIdMap, /// The parent of this node - parent_node: hir::ItemLocalId, + parent_node: ItemLocalId, owner: OwnerId, } @@ -31,17 +31,16 @@ pub(super) fn index_hir<'hir>( bodies: &SortedMap>, num_nodes: usize, ) -> (IndexVec>, LocalDefIdMap) { - let zero_id = ItemLocalId::new(0); - let err_node = ParentedNode { parent: zero_id, node: Node::Err(item.span()) }; + let err_node = ParentedNode { parent: ItemLocalId::ZERO, node: Node::Err(item.span()) }; let mut nodes = IndexVec::from_elem_n(err_node, num_nodes); // This node's parent should never be accessed: the owner's parent is computed by the // hir_owner_parent query. Make it invalid (= ItemLocalId::MAX) to force an ICE whenever it is // used. - nodes[zero_id] = ParentedNode { parent: ItemLocalId::INVALID, node: item.into() }; + nodes[ItemLocalId::ZERO] = ParentedNode { parent: ItemLocalId::INVALID, node: item.into() }; let mut collector = NodeCollector { tcx, owner: item.def_id(), - parent_node: zero_id, + parent_node: ItemLocalId::ZERO, nodes, bodies, parenting: Default::default(), @@ -112,7 +111,9 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> { } fn insert_nested(&mut self, item: LocalDefId) { - self.parenting.insert(item, self.parent_node); + if self.parent_node != ItemLocalId::ZERO { + self.parenting.insert(item, self.parent_node); + } } } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index c9786328565ba..abfea6078f21c 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -11,7 +11,7 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID}; use rustc_hir::PredicateOrigin; -use rustc_index::{Idx, IndexSlice, IndexVec}; +use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::span_bug; use rustc_middle::ty::{ResolverAstLowering, TyCtxt}; use rustc_span::edit_distance::find_best_match_for_name; @@ -563,7 +563,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let kind = this.lower_use_tree(use_tree, &prefix, id, vis_span, &mut ident, attrs); if let Some(attrs) = attrs { - this.attrs.insert(hir::ItemLocalId::new(0), attrs); + this.attrs.insert(hir::ItemLocalId::ZERO, attrs); } let item = hir::Item { diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index b5b98659e2fcc..8cf347bfa966c 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -157,7 +157,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { attrs: SortedMap::default(), children: Vec::default(), current_hir_id_owner: hir::CRATE_OWNER_ID, - item_local_id_counter: hir::ItemLocalId::new(0), + item_local_id_counter: hir::ItemLocalId::ZERO, node_id_to_local_id: Default::default(), trait_map: Default::default(), @@ -583,7 +583,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // and the caller to refer to some of the subdefinitions' nodes' `LocalDefId`s. // Always allocate the first `HirId` for the owner itself. - let _old = self.node_id_to_local_id.insert(owner, hir::ItemLocalId::new(0)); + let _old = self.node_id_to_local_id.insert(owner, hir::ItemLocalId::ZERO); debug_assert_eq!(_old, None); let item = f(self); @@ -677,7 +677,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { v.insert(local_id); self.item_local_id_counter.increment_by(1); - assert_ne!(local_id, hir::ItemLocalId::new(0)); + assert_ne!(local_id, hir::ItemLocalId::ZERO); if let Some(def_id) = self.opt_local_def_id(ast_node_id) { self.children.push((def_id, hir::MaybeOwner::NonOwner(hir_id))); } @@ -696,7 +696,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn next_id(&mut self) -> hir::HirId { let owner = self.current_hir_id_owner; let local_id = self.item_local_id_counter; - assert_ne!(local_id, hir::ItemLocalId::new(0)); + assert_ne!(local_id, hir::ItemLocalId::ZERO); self.item_local_id_counter.increment_by(1); hir::HirId { owner, local_id } } @@ -1847,8 +1847,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // the case where we have a mutable pattern to a reference as that would // no longer be an `ImplicitSelf`. TyKind::Ref(_, mt) if mt.ty.kind.is_implicit_self() => match mt.mutbl { - hir::Mutability::Not => hir::ImplicitSelfKind::ImmRef, - hir::Mutability::Mut => hir::ImplicitSelfKind::MutRef, + hir::Mutability::Not => hir::ImplicitSelfKind::RefImm, + hir::Mutability::Mut => hir::ImplicitSelfKind::RefMut, }, _ => hir::ImplicitSelfKind::None, } diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 28a13d275a559..ac3799e7a0565 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -68,7 +68,7 @@ ast_passes_extern_block_suggestion = if you meant to declare an externally defin ast_passes_extern_fn_qualifiers = functions in `extern` blocks cannot have qualifiers .label = in this `extern` block - .suggestion = remove the qualifiers + .suggestion = remove this qualifier ast_passes_extern_item_ascii = items in `extern` blocks cannot use non-ascii identifiers .label = in this `extern` block diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 3edb832b9a09b..093a985495c9f 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -514,13 +514,32 @@ impl<'a> AstValidator<'a> { } /// An `fn` in `extern { ... }` cannot have qualifiers, e.g. `async fn`. - fn check_foreign_fn_headerless(&self, ident: Ident, span: Span, header: FnHeader) { - if header.has_qualifiers() { + fn check_foreign_fn_headerless( + &self, + // Deconstruct to ensure exhaustiveness + FnHeader { unsafety, coroutine_kind, constness, ext }: FnHeader, + ) { + let report_err = |span| { self.dcx().emit_err(errors::FnQualifierInExtern { - span: ident.span, + span: span, block: self.current_extern_span(), - sugg_span: span.until(ident.span.shrink_to_lo()), }); + }; + match unsafety { + Unsafe::Yes(span) => report_err(span), + Unsafe::No => (), + } + match coroutine_kind { + Some(knd) => report_err(knd.span()), + None => (), + } + match constness { + Const::Yes(span) => report_err(span), + Const::No => (), + } + match ext { + Extern::None => (), + Extern::Implicit(span) | Extern::Explicit(_, span) => report_err(span), } } @@ -1145,7 +1164,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { ForeignItemKind::Fn(box Fn { defaultness, sig, body, .. }) => { self.check_defaultness(fi.span, *defaultness); self.check_foreign_fn_bodyless(fi.ident, body.as_deref()); - self.check_foreign_fn_headerless(fi.ident, fi.span, sig.header); + self.check_foreign_fn_headerless(sig.header); self.check_foreign_item_ascii_only(fi.ident); } ForeignItemKind::TyAlias(box TyAlias { @@ -1552,7 +1571,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { /// When encountering an equality constraint in a `where` clause, emit an error. If the code seems /// like it's setting an associated type, provide an appropriate suggestion. fn deny_equality_constraints( - this: &mut AstValidator<'_>, + this: &AstValidator<'_>, predicate: &WhereEqPredicate, generics: &Generics, ) { diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 9e8c1d7f5fd19..8ae9f7d3966f8 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -270,11 +270,10 @@ pub struct FnBodyInExtern { #[diag(ast_passes_extern_fn_qualifiers)] pub struct FnQualifierInExtern { #[primary_span] + #[suggestion(code = "", applicability = "maybe-incorrect")] pub span: Span, #[label] pub block: Span, - #[suggestion(code = "fn ", applicability = "maybe-incorrect", style = "verbose")] - pub sugg_span: Span, } #[derive(Diagnostic)] diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index bb83f4c0f0ef8..5912dd3f931b2 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -565,6 +565,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(unnamed_fields, "unnamed fields are not yet fully implemented"); gate_all!(fn_delegation, "functions delegation is not yet fully implemented"); gate_all!(postfix_match, "postfix match is experimental"); + gate_all!(mut_ref, "mutable by-reference bindings are experimental"); if !visitor.features.never_patterns { if let Some(spans) = spans.get(&sym::never_patterns) { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index a70daf1b644e3..3ea182c586752 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1545,12 +1545,15 @@ impl<'a> State<'a> { PatKind::Wild => self.word("_"), PatKind::Never => self.word("!"), PatKind::Ident(BindingAnnotation(by_ref, mutbl), ident, sub) => { - if *by_ref == ByRef::Yes { - self.word_nbsp("ref"); - } if mutbl.is_mut() { self.word_nbsp("mut"); } + if let ByRef::Yes(rmutbl) = by_ref { + self.word_nbsp("ref"); + if rmutbl.is_mut() { + self.word_nbsp("mut"); + } + } self.print_ident(*ident); if let Some(p) = sub { self.space(); diff --git a/compiler/rustc_attr/Cargo.toml b/compiler/rustc_attr/Cargo.toml index d33416d200380..3b24452450abe 100644 --- a/compiler/rustc_attr/Cargo.toml +++ b/compiler/rustc_attr/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start +rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 814104ec78c49..439f13e763570 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -1,5 +1,6 @@ //! Parsing and validation of builtin attributes +use rustc_abi::Align; use rustc_ast::{self as ast, attr}; use rustc_ast::{Attribute, LitKind, MetaItem, MetaItemKind, MetaItemLit, NestedMetaItem, NodeId}; use rustc_ast_pretty::pprust; @@ -919,10 +920,10 @@ pub enum ReprAttr { ReprInt(IntType), ReprRust, ReprC, - ReprPacked(u32), + ReprPacked(Align), ReprSimd, ReprTransparent, - ReprAlign(u32), + ReprAlign(Align), } #[derive(Eq, PartialEq, Debug, Copy, Clone)] @@ -968,7 +969,7 @@ pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec { let hint = match item.name_or_empty() { sym::Rust => Some(ReprRust), sym::C => Some(ReprC), - sym::packed => Some(ReprPacked(1)), + sym::packed => Some(ReprPacked(Align::ONE)), sym::simd => Some(ReprSimd), sym::transparent => Some(ReprTransparent), sym::align => { @@ -1209,11 +1210,17 @@ fn allow_unstable<'a>( }) } -pub fn parse_alignment(node: &ast::LitKind) -> Result { +pub fn parse_alignment(node: &ast::LitKind) -> Result { if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node { + // `Align::from_bytes` accepts 0 as an input, check is_power_of_two() first if literal.get().is_power_of_two() { - // rustc_middle::ty::layout::Align restricts align to <= 2^29 - if *literal <= 1 << 29 { Ok(literal.get() as u32) } else { Err("larger than 2^29") } + // Only possible error is larger than 2^29 + literal + .get() + .try_into() + .ok() + .and_then(|v| Align::from_bytes(v).ok()) + .ok_or("larger than 2^29") } else { Err("not a power of two") } diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index 6a683d129ded1..a38dd286be51b 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -159,7 +159,7 @@ impl<'tcx> BorrowSet<'tcx> { } pub(crate) fn indices(&self) -> impl Iterator { - BorrowIndex::from_usize(0)..BorrowIndex::from_usize(self.len()) + BorrowIndex::ZERO..BorrowIndex::from_usize(self.len()) } pub(crate) fn iter_enumerated(&self) -> impl Iterator)> { diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 578369de4d6e3..62e16d445c63f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -337,7 +337,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } fn suggest_ref_or_clone( - &mut self, + &self, mpi: MovePathIndex, move_span: Span, err: &mut Diag<'tcx>, @@ -1125,7 +1125,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } pub(crate) fn report_use_while_mutably_borrowed( - &mut self, + &self, location: Location, (place, _span): (Place<'tcx>, Span), borrow: &BorrowData<'tcx>, @@ -1174,7 +1174,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } pub(crate) fn report_conflicting_borrow( - &mut self, + &self, location: Location, (place, span): (Place<'tcx>, Span), gen_borrow_kind: BorrowKind, @@ -2463,7 +2463,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } fn report_local_value_does_not_live_long_enough( - &mut self, + &self, location: Location, name: &str, borrow: &BorrowData<'tcx>, @@ -2642,7 +2642,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } fn report_thread_local_value_does_not_live_long_enough( - &mut self, + &self, drop_span: Span, borrow_span: Span, ) -> Diag<'tcx> { @@ -2663,7 +2663,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { #[instrument(level = "debug", skip(self))] fn report_temporary_value_does_not_live_long_enough( - &mut self, + &self, location: Location, borrow: &BorrowData<'tcx>, drop_span: Span, @@ -2921,7 +2921,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { #[instrument(level = "debug", skip(self))] fn report_escaping_closure_capture( - &mut self, + &self, use_span: UseSpans<'tcx>, var_span: Span, fr_name: &RegionName, @@ -3031,7 +3031,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } fn report_escaping_data( - &mut self, + &self, borrow_span: Span, name: &Option, upvar_span: Span, @@ -3065,7 +3065,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } fn get_moved_indexes( - &mut self, + &self, location: Location, mpi: MovePathIndex, ) -> (Vec, Vec) { @@ -3854,7 +3854,7 @@ enum AnnotatedBorrowFnSignature<'tcx> { impl<'tcx> AnnotatedBorrowFnSignature<'tcx> { /// Annotate the provided diagnostic with information about borrow from the fn signature that /// helps explain. - pub(crate) fn emit(&self, cx: &mut MirBorrowckCtxt<'_, 'tcx>, diag: &mut Diag<'_>) -> String { + pub(crate) fn emit(&self, cx: &MirBorrowckCtxt<'_, 'tcx>, diag: &mut Diag<'_>) -> String { match self { &AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => { diag.span_label( diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 2aeea1dd341c4..bd068b29c1235 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -4,9 +4,8 @@ use core::ops::ControlFlow; use hir::{ExprKind, Param}; use rustc_errors::{Applicability, Diag}; -use rustc_hir as hir; use rustc_hir::intravisit::Visitor; -use rustc_hir::Node; +use rustc_hir::{self as hir, BindingAnnotation, ByRef, Node}; use rustc_infer::traits; use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem}; use rustc_middle::ty::{self, InstanceDef, ToPredicate, Ty, TyCtxt}; @@ -304,7 +303,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { { match *decl.local_info() { LocalInfo::User(BindingForm::Var(mir::VarBindingForm { - binding_mode: ty::BindingMode::BindByValue(Mutability::Not), + binding_mode: BindingAnnotation(ByRef::No, Mutability::Not), opt_ty_info: Some(sp), opt_match_place: _, pat_span: _, @@ -342,7 +341,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } else if decl.mutability.is_not() { if matches!( decl.local_info(), - LocalInfo::User(BindingForm::ImplicitSelf(hir::ImplicitSelfKind::MutRef)) + LocalInfo::User(BindingForm::ImplicitSelf(hir::ImplicitSelfKind::RefMut)) ) { err.note( "as `Self` may be unsized, this call attempts to take `&mut &mut self`", @@ -407,7 +406,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if let Some(fn_decl) = node.fn_decl() { if !matches!( fn_decl.implicit_self, - hir::ImplicitSelfKind::ImmRef | hir::ImplicitSelfKind::MutRef + hir::ImplicitSelfKind::RefImm | hir::ImplicitSelfKind::RefMut ) { err.span_suggestion( upvar_ident.span, @@ -541,19 +540,23 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } } + /// Suggest `map[k] = v` => `map.insert(k, v)` and the like. fn suggest_map_index_mut_alternatives(&self, ty: Ty<'tcx>, err: &mut Diag<'tcx>, span: Span) { let Some(adt) = ty.ty_adt_def() else { return }; let did = adt.did(); if self.infcx.tcx.is_diagnostic_item(sym::HashMap, did) || self.infcx.tcx.is_diagnostic_item(sym::BTreeMap, did) { - struct V<'a, 'tcx> { + /// Walks through the HIR, looking for the corresponding span for this error. + /// When it finds it, see if it corresponds to assignment operator whose LHS + /// is an index expr. + struct SuggestIndexOperatorAlternativeVisitor<'a, 'tcx> { assign_span: Span, err: &'a mut Diag<'tcx>, ty: Ty<'tcx>, suggested: bool, } - impl<'a, 'tcx> Visitor<'tcx> for V<'a, 'tcx> { + impl<'a, 'tcx> Visitor<'tcx> for SuggestIndexOperatorAlternativeVisitor<'a, 'tcx> { fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) { hir::intravisit::walk_stmt(self, stmt); let expr = match stmt.kind { @@ -646,7 +649,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let Some(body_id) = hir_map.maybe_body_owned_by(local_def_id) else { return }; let body = self.infcx.tcx.hir().body(body_id); - let mut v = V { assign_span: span, err, ty, suggested: false }; + let mut v = SuggestIndexOperatorAlternativeVisitor { + assign_span: span, + err, + ty, + suggested: false, + }; v.visit_body(body); if !v.suggested { err.help(format!( @@ -717,7 +725,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { debug!("local_decl: {:?}", local_decl); let pat_span = match *local_decl.local_info() { LocalInfo::User(BindingForm::Var(mir::VarBindingForm { - binding_mode: ty::BindingMode::BindByValue(Mutability::Not), + binding_mode: BindingAnnotation(ByRef::No, Mutability::Not), opt_ty_info: _, opt_match_place: _, pat_span, @@ -1070,7 +1078,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm { - binding_mode: ty::BindingMode::BindByValue(_), + binding_mode: BindingAnnotation(ByRef::No, _), opt_ty_info, .. })) => { @@ -1138,7 +1146,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm { - binding_mode: ty::BindingMode::BindByReference(_), + binding_mode: BindingAnnotation(ByRef::Yes(_), _), .. })) => { let pattern_span: Span = local_decl.source_info.span; @@ -1329,7 +1337,7 @@ pub fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option< match *local_decl.local_info() { // Check if mutably borrowing a mutable reference. LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm { - binding_mode: ty::BindingMode::BindByValue(Mutability::Not), + binding_mode: BindingAnnotation(ByRef::No, Mutability::Not), .. })) => matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut)), LocalInfo::User(mir::BindingForm::ImplicitSelf(kind)) => { @@ -1338,7 +1346,7 @@ pub fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option< // // Deliberately fall into this case for all implicit self types, // so that we don't fall into the next case with them. - kind == hir::ImplicitSelfKind::MutRef + kind == hir::ImplicitSelfKind::RefMut } _ if Some(kw::SelfLower) == local_name => { // Otherwise, check if the name is the `self` keyword - in which case diff --git a/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs b/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs index 45f7b07fd5f93..7f5302270439c 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs @@ -87,6 +87,8 @@ pub(super) fn populate_access_facts<'a, 'tcx>( body: &Body<'tcx>, location_table: &LocationTable, move_data: &MoveData<'tcx>, + //FIXME: this is not mutated, but expected to be modified as + // out param, bug? dropped_at: &mut Vec<(Local, Location)>, ) { debug!("populate_access_facts()"); diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 18975a4e3b271..8bdefdfc0ac99 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -200,7 +200,7 @@ impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> { for local in boring_locals { let local_ty = self.cx.body.local_decls[local].ty; let drop_data = self.cx.drop_data.entry(local_ty).or_insert_with({ - let typeck = &mut self.cx.typeck; + let typeck = &self.cx.typeck; move || LivenessContext::compute_drop_data(typeck, local_ty) }); @@ -542,7 +542,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { ); let drop_data = self.drop_data.entry(dropped_ty).or_insert_with({ - let typeck = &mut self.typeck; + let typeck = &self.typeck; move || Self::compute_drop_data(typeck, dropped_ty) }); @@ -597,10 +597,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { }); } - fn compute_drop_data( - typeck: &mut TypeChecker<'_, 'tcx>, - dropped_ty: Ty<'tcx>, - ) -> DropData<'tcx> { + fn compute_drop_data(typeck: &TypeChecker<'_, 'tcx>, dropped_ty: Ty<'tcx>) -> DropData<'tcx> { debug!("compute_drop_data(dropped_ty={:?})", dropped_ty,); match typeck diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index b0bdf4af0975f..71b54a761a2be 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -2261,7 +2261,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } - CastKind::PointerExposeAddress => { + CastKind::PointerExposeProvenance => { let ty_from = op.ty(body, tcx); let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); @@ -2271,7 +2271,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { span_mirbug!( self, rvalue, - "Invalid PointerExposeAddress cast {:?} -> {:?}", + "Invalid PointerExposeProvenance cast {:?} -> {:?}", ty_from, ty ) @@ -2279,7 +2279,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } - CastKind::PointerFromExposedAddress => { + CastKind::PointerWithExposedProvenance => { let ty_from = op.ty(body, tcx); let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); @@ -2289,7 +2289,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { span_mirbug!( self, rvalue, - "Invalid PointerFromExposedAddress cast {:?} -> {:?}", + "Invalid PointerWithExposedProvenance cast {:?} -> {:?}", ty_from, ty ) diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index ba8401393d7ab..2a5bc58af3b3d 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -114,6 +114,8 @@ builtin_macros_env_not_defined = environment variable `{$var}` not defined at co .cargo = Cargo sets build script variables at run time. Use `std::env::var({$var_expr})` instead .custom = use `std::env::var({$var_expr})` to read the variable at run time +builtin_macros_env_not_unicode = environment variable `{$var}` is not a valid Unicode string + builtin_macros_env_takes_args = `env!()` takes 1 or 2 arguments builtin_macros_expected_one_cfg_pattern = expected 1 cfg-pattern diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 76805617c933b..137ac44157924 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -29,7 +29,7 @@ pub struct AsmArgs { } fn parse_args<'a>( - ecx: &mut ExtCtxt<'a>, + ecx: &ExtCtxt<'a>, sp: Span, tts: TokenStream, is_global_asm: bool, @@ -303,7 +303,7 @@ pub fn parse_asm_args<'a>( /// /// This function must be called immediately after the option token is parsed. /// Otherwise, the suggestion will be incorrect. -fn err_duplicate_option(p: &mut Parser<'_>, symbol: Symbol, span: Span) { +fn err_duplicate_option(p: &Parser<'_>, symbol: Symbol, span: Span) { // Tool-only output let full_span = if p.token.kind == token::Comma { span.to(p.token.span) } else { span }; p.psess.dcx.emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span }); @@ -315,7 +315,7 @@ fn err_duplicate_option(p: &mut Parser<'_>, symbol: Symbol, span: Span) { /// This function must be called immediately after the option token is parsed. /// Otherwise, the error will not point to the correct spot. fn try_set_option<'a>( - p: &mut Parser<'a>, + p: &Parser<'a>, args: &mut AsmArgs, symbol: Symbol, option: ast::InlineAsmOptions, diff --git a/compiler/rustc_builtin_macros/src/assert.rs b/compiler/rustc_builtin_macros/src/assert.rs index 5905bdd710849..d200179f3a0ae 100644 --- a/compiler/rustc_builtin_macros/src/assert.rs +++ b/compiler/rustc_builtin_macros/src/assert.rs @@ -111,7 +111,7 @@ fn expr_if_not( cx.expr_if(span, cx.expr(span, ExprKind::Unary(UnOp::Not, cond)), then, els) } -fn parse_assert<'a>(cx: &mut ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult<'a, Assert> { +fn parse_assert<'a>(cx: &ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult<'a, Assert> { let mut parser = cx.new_parser_from_tts(stream); if parser.token == token::Eof { diff --git a/compiler/rustc_builtin_macros/src/cfg.rs b/compiler/rustc_builtin_macros/src/cfg.rs index 9197b9ebdf9fc..5dc9bbacd0629 100644 --- a/compiler/rustc_builtin_macros/src/cfg.rs +++ b/compiler/rustc_builtin_macros/src/cfg.rs @@ -35,7 +35,7 @@ pub fn expand_cfg( }) } -fn parse_cfg<'a>(cx: &mut ExtCtxt<'a>, span: Span, tts: TokenStream) -> PResult<'a, ast::MetaItem> { +fn parse_cfg<'a>(cx: &ExtCtxt<'a>, span: Span, tts: TokenStream) -> PResult<'a, ast::MetaItem> { let mut p = cx.new_parser_from_tts(tts); if p.token == token::Eof { diff --git a/compiler/rustc_builtin_macros/src/cfg_accessible.rs b/compiler/rustc_builtin_macros/src/cfg_accessible.rs index 1933b2e1fb7fe..98c0ca3a526b8 100644 --- a/compiler/rustc_builtin_macros/src/cfg_accessible.rs +++ b/compiler/rustc_builtin_macros/src/cfg_accessible.rs @@ -10,7 +10,7 @@ use rustc_span::Span; pub(crate) struct Expander; -fn validate_input<'a>(ecx: &mut ExtCtxt<'_>, mi: &'a ast::MetaItem) -> Option<&'a ast::Path> { +fn validate_input<'a>(ecx: &ExtCtxt<'_>, mi: &'a ast::MetaItem) -> Option<&'a ast::Path> { use errors::CfgAccessibleInvalid::*; match mi.meta_item_list() { None => {} diff --git a/compiler/rustc_builtin_macros/src/concat_bytes.rs b/compiler/rustc_builtin_macros/src/concat_bytes.rs index a2f827c5567d8..45fec2945788b 100644 --- a/compiler/rustc_builtin_macros/src/concat_bytes.rs +++ b/compiler/rustc_builtin_macros/src/concat_bytes.rs @@ -8,7 +8,7 @@ use crate::errors; /// Emits errors for literal expressions that are invalid inside and outside of an array. fn invalid_type_err( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, token_lit: token::Lit, span: Span, is_nested: bool, @@ -65,7 +65,7 @@ fn invalid_type_err( /// Otherwise, returns `None`, and either pushes the `expr`'s span to `missing_literals` or /// updates `guar` accordingly. fn handle_array_element( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, guar: &mut Option, missing_literals: &mut Vec, expr: &P, diff --git a/compiler/rustc_builtin_macros/src/deriving/bounds.rs b/compiler/rustc_builtin_macros/src/deriving/bounds.rs index 8027ca2e7bb47..26ef3da3a915c 100644 --- a/compiler/rustc_builtin_macros/src/deriving/bounds.rs +++ b/compiler/rustc_builtin_macros/src/deriving/bounds.rs @@ -6,7 +6,7 @@ use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::Span; pub fn expand_deriving_copy( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, span: Span, mitem: &MetaItem, item: &Annotatable, @@ -29,7 +29,7 @@ pub fn expand_deriving_copy( } pub fn expand_deriving_const_param_ty( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, span: Span, mitem: &MetaItem, item: &Annotatable, diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs index 267405ac32e24..0a44bd42b9173 100644 --- a/compiler/rustc_builtin_macros/src/deriving/clone.rs +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -9,7 +9,7 @@ use rustc_span::Span; use thin_vec::{thin_vec, ThinVec}; pub fn expand_deriving_clone( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, span: Span, mitem: &MetaItem, item: &Annotatable, @@ -94,7 +94,7 @@ pub fn expand_deriving_clone( fn cs_clone_simple( name: &str, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'_>, is_union: bool, @@ -157,14 +157,14 @@ fn cs_clone_simple( fn cs_clone( name: &str, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'_>, ) -> BlockOrExpr { let ctor_path; let all_fields; let fn_path = cx.std_path(&[sym::clone, sym::Clone, sym::clone]); - let subcall = |cx: &mut ExtCtxt<'_>, field: &FieldInfo| { + let subcall = |cx: &ExtCtxt<'_>, field: &FieldInfo| { let args = thin_vec![field.self_expr.clone()]; cx.expr_call_global(field.span, fn_path.clone(), args) }; diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs index ce3fa1ab32c6a..45c4467a1097c 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs @@ -10,7 +10,7 @@ use rustc_span::Span; use thin_vec::{thin_vec, ThinVec}; pub fn expand_deriving_eq( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, span: Span, mitem: &MetaItem, item: &Annotatable, @@ -49,7 +49,7 @@ pub fn expand_deriving_eq( } fn cs_total_eq_assert( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'_>, ) -> BlockOrExpr { diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs index 0923acfeeddb8..1d7a69540ab89 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs @@ -8,7 +8,7 @@ use rustc_span::Span; use thin_vec::thin_vec; pub fn expand_deriving_ord( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, span: Span, mitem: &MetaItem, item: &Annotatable, @@ -39,7 +39,7 @@ pub fn expand_deriving_ord( trait_def.expand(cx, mitem, item, push) } -pub fn cs_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr { +pub fn cs_cmp(cx: &ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr { let test_id = Ident::new(sym::cmp, span); let equal_path = cx.path_global(span, cx.std_path(&[sym::cmp, sym::Ordering, sym::Equal])); let cmp_path = cx.std_path(&[sym::cmp, sym::Ord, sym::cmp]); diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs index 006110cd4b1f6..234918ae42961 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs @@ -9,14 +9,14 @@ use rustc_span::Span; use thin_vec::thin_vec; pub fn expand_deriving_partial_eq( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, span: Span, mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), is_const: bool, ) { - fn cs_eq(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr { + fn cs_eq(cx: &ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr { let base = true; let expr = cs_fold( true, // use foldl diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs index 60dbdf8b54460..49fe89b18b091 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs @@ -8,7 +8,7 @@ use rustc_span::Span; use thin_vec::thin_vec; pub fn expand_deriving_partial_ord( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, span: Span, mitem: &MetaItem, item: &Annotatable, @@ -69,7 +69,7 @@ pub fn expand_deriving_partial_ord( } fn cs_partial_cmp( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, span: Span, substr: &Substructure<'_>, tag_then_data: bool, diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs index 03acd7f489f29..e442b3520b275 100644 --- a/compiler/rustc_builtin_macros/src/deriving/debug.rs +++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs @@ -9,7 +9,7 @@ use rustc_span::Span; use thin_vec::{thin_vec, ThinVec}; pub fn expand_deriving_debug( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, span: Span, mitem: &MetaItem, item: &Annotatable, @@ -45,7 +45,7 @@ pub fn expand_deriving_debug( trait_def.expand(cx, mitem, item, push) } -fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr { +fn show_substructure(cx: &ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr { // We want to make sure we have the ctxt set so that we can use unstable methods let span = cx.with_def_site_ctxt(span); @@ -209,7 +209,7 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_> /// } /// ``` fn show_fieldless_enum( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, span: Span, def: &EnumDef, substr: &Substructure<'_>, diff --git a/compiler/rustc_builtin_macros/src/deriving/decodable.rs b/compiler/rustc_builtin_macros/src/deriving/decodable.rs index bf4693cd54198..34798ab0a17b0 100644 --- a/compiler/rustc_builtin_macros/src/deriving/decodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/decodable.rs @@ -11,7 +11,7 @@ use rustc_span::Span; use thin_vec::{thin_vec, ThinVec}; pub fn expand_deriving_rustc_decodable( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, span: Span, mitem: &MetaItem, item: &Annotatable, @@ -63,7 +63,7 @@ pub fn expand_deriving_rustc_decodable( } fn decodable_substructure( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'_>, krate: Symbol, @@ -186,14 +186,14 @@ fn decodable_substructure( /// - `outer_pat_path` is the path to this enum variant/struct /// - `getarg` should retrieve the `usize`-th field with name `@str`. fn decode_static_fields( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_span: Span, outer_pat_path: ast::Path, fields: &StaticFields, mut getarg: F, ) -> P where - F: FnMut(&mut ExtCtxt<'_>, Span, Symbol, usize) -> P, + F: FnMut(&ExtCtxt<'_>, Span, Symbol, usize) -> P, { match fields { Unnamed(fields, is_tuple) => { diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index 292a916e2a78b..328770ce10d2c 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -13,7 +13,7 @@ use smallvec::SmallVec; use thin_vec::{thin_vec, ThinVec}; pub fn expand_deriving_default( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, span: Span, mitem: &ast::MetaItem, item: &Annotatable, @@ -54,7 +54,7 @@ pub fn expand_deriving_default( } fn default_struct_substructure( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'_>, summary: &StaticFields, @@ -81,7 +81,7 @@ fn default_struct_substructure( } fn default_enum_substructure( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_span: Span, enum_def: &EnumDef, ) -> BlockOrExpr { @@ -103,7 +103,7 @@ fn default_enum_substructure( } fn extract_default_variant<'a>( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, enum_def: &'a EnumDef, trait_span: Span, ) -> Result<&'a rustc_ast::Variant, ErrorGuaranteed> { @@ -173,7 +173,7 @@ fn extract_default_variant<'a>( } fn validate_default_attribute( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, default_variant: &rustc_ast::Variant, ) -> Result<(), ErrorGuaranteed> { let attrs: SmallVec<[_; 1]> = diff --git a/compiler/rustc_builtin_macros/src/deriving/encodable.rs b/compiler/rustc_builtin_macros/src/deriving/encodable.rs index d939f8c7aeb1d..2e5f1173825a1 100644 --- a/compiler/rustc_builtin_macros/src/deriving/encodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/encodable.rs @@ -95,7 +95,7 @@ use rustc_span::Span; use thin_vec::{thin_vec, ThinVec}; pub fn expand_deriving_rustc_encodable( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, span: Span, mitem: &MetaItem, item: &Annotatable, @@ -147,7 +147,7 @@ pub fn expand_deriving_rustc_encodable( } fn encodable_substructure( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'_>, krate: Symbol, diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index afa73b672da13..e16d74eed4e07 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -330,7 +330,7 @@ pub enum SubstructureFields<'a> { /// Combine the values of all the fields together. The last argument is /// all the fields of all the structures. pub type CombineSubstructureFunc<'a> = - Box, Span, &Substructure<'_>) -> BlockOrExpr + 'a>; + Box, Span, &Substructure<'_>) -> BlockOrExpr + 'a>; pub fn combine_substructure( f: CombineSubstructureFunc<'_>, @@ -454,7 +454,7 @@ fn find_type_parameters( impl<'a> TraitDef<'a> { pub fn expand( self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, mitem: &ast::MetaItem, item: &'a Annotatable, push: &mut dyn FnMut(Annotatable), @@ -464,7 +464,7 @@ impl<'a> TraitDef<'a> { pub fn expand_ext( self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, mitem: &ast::MetaItem, item: &'a Annotatable, push: &mut dyn FnMut(Annotatable), @@ -577,7 +577,7 @@ impl<'a> TraitDef<'a> { /// therefore does not get bound by the derived trait. fn create_derived_impl( &self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, type_ident: Ident, generics: &Generics, field_tys: Vec>, @@ -802,7 +802,7 @@ impl<'a> TraitDef<'a> { fn expand_struct_def( &self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, struct_def: &'a VariantData, type_ident: Ident, generics: &Generics, @@ -856,7 +856,7 @@ impl<'a> TraitDef<'a> { fn expand_enum_def( &self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, enum_def: &'a EnumDef, type_ident: Ident, generics: &Generics, @@ -914,7 +914,7 @@ impl<'a> TraitDef<'a> { impl<'a> MethodDef<'a> { fn call_substructure_method( &self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_: &TraitDef<'_>, type_ident: Ident, nonselflike_args: &[P], @@ -929,7 +929,7 @@ impl<'a> MethodDef<'a> { fn get_ret_ty( &self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_: &TraitDef<'_>, generics: &Generics, type_ident: Ident, @@ -950,7 +950,7 @@ impl<'a> MethodDef<'a> { // `&self`. fn extract_arg_details( &self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_: &TraitDef<'_>, type_ident: Ident, generics: &Generics, @@ -986,7 +986,7 @@ impl<'a> MethodDef<'a> { fn create_method( &self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_: &TraitDef<'_>, type_ident: Ident, generics: &Generics, @@ -1077,7 +1077,7 @@ impl<'a> MethodDef<'a> { /// ``` fn expand_struct_method_body<'b>( &self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_: &TraitDef<'b>, struct_def: &'b VariantData, type_ident: Ident, @@ -1100,7 +1100,7 @@ impl<'a> MethodDef<'a> { fn expand_static_struct_method_body( &self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_: &TraitDef<'_>, struct_def: &VariantData, type_ident: Ident, @@ -1154,7 +1154,7 @@ impl<'a> MethodDef<'a> { /// `Unify`), and possibly a default arm. fn expand_enum_method_body<'b>( &self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_: &TraitDef<'b>, enum_def: &'b EnumDef, type_ident: Ident, @@ -1403,7 +1403,7 @@ impl<'a> MethodDef<'a> { fn expand_static_enum_method_body( &self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_: &TraitDef<'_>, enum_def: &EnumDef, type_ident: Ident, @@ -1430,7 +1430,7 @@ impl<'a> MethodDef<'a> { // general helper methods. impl<'a> TraitDef<'a> { - fn summarise_struct(&self, cx: &mut ExtCtxt<'_>, struct_def: &VariantData) -> StaticFields { + fn summarise_struct(&self, cx: &ExtCtxt<'_>, struct_def: &VariantData) -> StaticFields { let mut named_idents = Vec::new(); let mut just_spans = Vec::new(); for field in struct_def.fields() { @@ -1460,7 +1460,7 @@ impl<'a> TraitDef<'a> { fn create_struct_patterns( &self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, struct_path: ast::Path, struct_def: &'a VariantData, prefixes: &[String], @@ -1553,7 +1553,7 @@ impl<'a> TraitDef<'a> { fn create_struct_pattern_fields( &self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, struct_def: &'a VariantData, prefixes: &[String], ) -> Vec { @@ -1570,7 +1570,7 @@ impl<'a> TraitDef<'a> { fn create_struct_field_access_fields( &self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, selflike_args: &[P], struct_def: &'a VariantData, is_packed: bool, @@ -1668,13 +1668,13 @@ pub enum CsFold<'a> { /// Statics may not be folded over. pub fn cs_fold( use_foldl: bool, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_span: Span, substructure: &Substructure<'_>, mut f: F, ) -> P where - F: FnMut(&mut ExtCtxt<'_>, CsFold<'_>) -> P, + F: FnMut(&ExtCtxt<'_>, CsFold<'_>) -> P, { match substructure.fields { EnumMatching(.., all_fields) | Struct(_, all_fields) => { diff --git a/compiler/rustc_builtin_macros/src/deriving/hash.rs b/compiler/rustc_builtin_macros/src/deriving/hash.rs index dd6149cf61496..6bb61311bd281 100644 --- a/compiler/rustc_builtin_macros/src/deriving/hash.rs +++ b/compiler/rustc_builtin_macros/src/deriving/hash.rs @@ -8,7 +8,7 @@ use rustc_span::Span; use thin_vec::thin_vec; pub fn expand_deriving_hash( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, span: Span, mitem: &MetaItem, item: &Annotatable, @@ -46,11 +46,7 @@ pub fn expand_deriving_hash( hash_trait_def.expand(cx, mitem, item, push); } -fn hash_substructure( - cx: &mut ExtCtxt<'_>, - trait_span: Span, - substr: &Substructure<'_>, -) -> BlockOrExpr { +fn hash_substructure(cx: &ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'_>) -> BlockOrExpr { let [state_expr] = substr.nonselflike_args else { cx.dcx().span_bug(trait_span, "incorrect number of arguments in `derive(Hash)`"); }; diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index 8a3375cba9dc1..9f786d22c932f 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -40,7 +40,7 @@ pub mod partial_ord; pub mod generic; pub(crate) type BuiltinDeriveFn = - fn(&mut ExtCtxt<'_>, Span, &MetaItem, &Annotatable, &mut dyn FnMut(Annotatable), bool); + fn(&ExtCtxt<'_>, Span, &MetaItem, &Annotatable, &mut dyn FnMut(Annotatable), bool); pub(crate) struct BuiltinDerive(pub(crate) BuiltinDeriveFn); @@ -117,7 +117,7 @@ fn call_unreachable(cx: &ExtCtxt<'_>, span: Span) -> P { } fn assert_ty_bounds( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, stmts: &mut ThinVec, ty: P, span: Span, diff --git a/compiler/rustc_builtin_macros/src/edition_panic.rs b/compiler/rustc_builtin_macros/src/edition_panic.rs index fa22e91164232..bb3c83e8c0e3f 100644 --- a/compiler/rustc_builtin_macros/src/edition_panic.rs +++ b/compiler/rustc_builtin_macros/src/edition_panic.rs @@ -40,7 +40,7 @@ pub fn expand_unreachable<'cx>( fn expand<'cx>( mac: rustc_span::Symbol, - cx: &'cx mut ExtCtxt<'_>, + cx: &'cx ExtCtxt<'_>, sp: Span, tts: TokenStream, ) -> MacroExpanderResult<'cx> { diff --git a/compiler/rustc_builtin_macros/src/env.rs b/compiler/rustc_builtin_macros/src/env.rs index bce710e5cab12..9387304594378 100644 --- a/compiler/rustc_builtin_macros/src/env.rs +++ b/compiler/rustc_builtin_macros/src/env.rs @@ -11,18 +11,19 @@ use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpa use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; use std::env; +use std::env::VarError; use thin_vec::thin_vec; use crate::errors; -fn lookup_env<'cx>(cx: &'cx ExtCtxt<'_>, var: Symbol) -> Option { +fn lookup_env<'cx>(cx: &'cx ExtCtxt<'_>, var: Symbol) -> Result { let var = var.as_str(); if let Some(value) = cx.sess.opts.logical_env.get(var) { - return Some(Symbol::intern(value)); + return Ok(Symbol::intern(value)); } // If the environment variable was not defined with the `--env-set` option, we try to retrieve it // from rustc's environment. - env::var(var).ok().as_deref().map(Symbol::intern) + Ok(Symbol::intern(&env::var(var)?)) } pub fn expand_option_env<'cx>( @@ -39,7 +40,7 @@ pub fn expand_option_env<'cx>( }; let sp = cx.with_def_site_ctxt(sp); - let value = lookup_env(cx, var); + let value = lookup_env(cx, var).ok(); cx.sess.psess.env_depinfo.borrow_mut().insert((var, value)); let e = match value { None => { @@ -108,9 +109,9 @@ pub fn expand_env<'cx>( let span = cx.with_def_site_ctxt(sp); let value = lookup_env(cx, var); - cx.sess.psess.env_depinfo.borrow_mut().insert((var, value)); + cx.sess.psess.env_depinfo.borrow_mut().insert((var, value.as_ref().ok().copied())); let e = match value { - None => { + Err(err) => { let ExprKind::Lit(token::Lit { kind: LitKind::Str | LitKind::StrRaw(..), symbol, .. }) = &var_expr.kind @@ -118,25 +119,33 @@ pub fn expand_env<'cx>( unreachable!("`expr_to_string` ensures this is a string lit") }; - let guar = if let Some(msg_from_user) = custom_msg { - cx.dcx().emit_err(errors::EnvNotDefinedWithUserMessage { span, msg_from_user }) - } else if is_cargo_env_var(var.as_str()) { - cx.dcx().emit_err(errors::EnvNotDefined::CargoEnvVar { - span, - var: *symbol, - var_expr: var_expr.ast_deref(), - }) - } else { - cx.dcx().emit_err(errors::EnvNotDefined::CustomEnvVar { - span, - var: *symbol, - var_expr: var_expr.ast_deref(), - }) + let guar = match err { + VarError::NotPresent => { + if let Some(msg_from_user) = custom_msg { + cx.dcx() + .emit_err(errors::EnvNotDefinedWithUserMessage { span, msg_from_user }) + } else if is_cargo_env_var(var.as_str()) { + cx.dcx().emit_err(errors::EnvNotDefined::CargoEnvVar { + span, + var: *symbol, + var_expr: var_expr.ast_deref(), + }) + } else { + cx.dcx().emit_err(errors::EnvNotDefined::CustomEnvVar { + span, + var: *symbol, + var_expr: var_expr.ast_deref(), + }) + } + } + VarError::NotUnicode(_) => { + cx.dcx().emit_err(errors::EnvNotUnicode { span, var: *symbol }) + } }; return ExpandResult::Ready(DummyResult::any(sp, guar)); } - Some(value) => cx.expr_str(span, value), + Ok(value) => cx.expr_str(span, value), }; ExpandResult::Ready(MacEager::expr(e)) } diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 377aff8fb6c00..6b6647ef085c2 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -458,6 +458,14 @@ pub(crate) enum EnvNotDefined<'a> { }, } +#[derive(Diagnostic)] +#[diag(builtin_macros_env_not_unicode)] +pub(crate) struct EnvNotUnicode { + #[primary_span] + pub(crate) span: Span, + pub(crate) var: Symbol, +} + #[derive(Diagnostic)] #[diag(builtin_macros_format_requires_string)] pub(crate) struct FormatRequiresString { diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 6f031f270cae5..51d6058a744fc 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -65,7 +65,7 @@ struct MacroInput { /// ```text /// Ok((fmtstr, parsed arguments)) /// ``` -fn parse_args<'a>(ecx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a, MacroInput> { +fn parse_args<'a>(ecx: &ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a, MacroInput> { let mut args = FormatArguments::new(); let mut p = ecx.new_parser_from_tts(tts); @@ -604,7 +604,7 @@ fn invalid_placeholder_type_error( } fn report_missing_placeholders( - ecx: &mut ExtCtxt<'_>, + ecx: &ExtCtxt<'_>, unused: Vec<(Span, bool)>, used: &[bool], args: &FormatArguments, @@ -734,7 +734,7 @@ fn report_missing_placeholders( /// This function detects and reports unused format!() arguments that are /// redundant due to implicit captures (e.g. `format!("{x}", x)`). fn report_redundant_format_arguments<'a>( - ecx: &mut ExtCtxt<'a>, + ecx: &ExtCtxt<'a>, args: &FormatArguments, used: &[bool], placeholders: Vec<(Span, &str)>, @@ -806,7 +806,7 @@ fn report_redundant_format_arguments<'a>( /// there are named arguments or numbered positional arguments in the /// format string. fn report_invalid_references( - ecx: &mut ExtCtxt<'_>, + ecx: &ExtCtxt<'_>, invalid_refs: &[(usize, Option, PositionUsedAs, FormatArgPositionKind)], template: &[FormatArgsPiece], fmt_span: Span, diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index abcdfabcaedae..61a6361ae8dcf 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -234,7 +234,7 @@ pub fn expand_include_bytes( } fn load_binary_file( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, original_path: &Path, macro_span: Span, path_span: Span, diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index 81ac78dd58fa6..c7568f1461c10 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -96,7 +96,7 @@ pub fn expand_bench( } pub fn expand_test_or_bench( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, attr_sp: Span, item: Annotatable, is_bench: bool, diff --git a/compiler/rustc_codegen_cranelift/.cirrus.yml b/compiler/rustc_codegen_cranelift/.cirrus.yml index aa1a2bad2cf2a..97c2f45d31e22 100644 --- a/compiler/rustc_codegen_cranelift/.cirrus.yml +++ b/compiler/rustc_codegen_cranelift/.cirrus.yml @@ -13,4 +13,7 @@ task: - ./y.sh prepare test_script: - . $HOME/.cargo/env + # Disabling incr comp reduces cache size and incr comp doesn't save as much + # on CI anyway. + - export CARGO_BUILD_INCREMENTAL=false - ./y.sh test diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml b/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml index e6bf944f5527b..a745f2801cc4e 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml @@ -3,6 +3,8 @@ name: Abi-cafe on: - push +permissions: {} + jobs: abi_cafe: runs-on: ${{ matrix.os }} diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml index 526871d0c05f4..913a5c5a8500c 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml @@ -4,6 +4,20 @@ on: - push - pull_request +defaults: + run: + shell: bash + +permissions: {} + +env: + # Disabling incr comp reduces cache size and incr comp doesn't save as much + # on CI anyway. + CARGO_BUILD_INCREMENTAL: false + # Rust's CI denies warnings. Deny them here too to ensure subtree syncs don't + # fail because of warnings. + RUSTFLAGS: "-Dwarnings" + jobs: rustfmt: runs-on: ubuntu-latest @@ -23,15 +37,15 @@ jobs: cargo fmt --check rustfmt --check build_system/main.rs rustfmt --check example/* + rustfmt --check scripts/*.rs test: runs-on: ${{ matrix.os }} timeout-minutes: 60 - defaults: - run: - shell: bash + env: + CG_CLIF_EXPENSIVE_CHECKS: 1 strategy: fail-fast: false @@ -47,15 +61,19 @@ jobs: - 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 + apt_deps: gcc-aarch64-linux-gnu qemu-user - os: ubuntu-latest env: TARGET_TRIPLE: s390x-unknown-linux-gnu + apt_deps: gcc-s390x-linux-gnu qemu-user - os: ubuntu-latest env: TARGET_TRIPLE: riscv64gc-unknown-linux-gnu + apt_deps: gcc-riscv64-linux-gnu qemu-user - os: windows-latest env: TARGET_TRIPLE: x86_64-pc-windows-msvc @@ -80,29 +98,11 @@ jobs: if: matrix.os == 'windows-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu' run: rustup set default-host x86_64-pc-windows-gnu - - name: Install MinGW toolchain and wine - 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 wine-stable - - - name: Install AArch64 toolchain and qemu - if: matrix.os == 'ubuntu-latest' && matrix.env.TARGET_TRIPLE == 'aarch64-unknown-linux-gnu' - run: | - sudo apt-get update - sudo apt-get install -y gcc-aarch64-linux-gnu qemu-user - - - name: Install s390x toolchain and qemu - if: matrix.env.TARGET_TRIPLE == 's390x-unknown-linux-gnu' + - name: Install toolchain and emulator + if: matrix.apt_deps != null run: | sudo apt-get update - sudo apt-get install -y gcc-s390x-linux-gnu qemu-user - - - name: Install riscv64gc toolchain and qemu - if: matrix.env.TARGET_TRIPLE == 'riscv64gc-unknown-linux-gnu' - run: | - sudo apt-get update - sudo apt-get install -y gcc-riscv64-linux-gnu qemu-user + sudo apt-get install -y ${{ matrix.apt_deps }} - name: Prepare dependencies run: ./y.sh prepare @@ -142,10 +142,6 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 60 - defaults: - run: - shell: bash - steps: - uses: actions/checkout@v4 @@ -168,10 +164,6 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 60 - defaults: - run: - shell: bash - steps: - uses: actions/checkout@v4 @@ -193,20 +185,16 @@ jobs: run: ./y.sh prepare - name: Build - run: CI_OPT=1 ./y.sh build --sysroot none + run: ./y.sh build --sysroot none - name: Benchmark - run: CI_OPT=1 ./y.sh bench + run: ./y.sh bench dist: runs-on: ${{ matrix.os }} timeout-minutes: 60 - defaults: - run: - shell: bash - strategy: fail-fast: false matrix: @@ -252,10 +240,10 @@ jobs: run: ./y.sh prepare - name: Build backend - run: CI_OPT=1 ./y.sh build --sysroot none + run: ./y.sh build --sysroot none - name: Build sysroot - run: CI_OPT=1 ./y.sh build + run: ./y.sh build - name: Package prebuilt cg_clif run: tar cvfJ cg_clif.tar.xz dist diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml b/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml index 930d025b73edc..75ea94ee79790 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml @@ -3,6 +3,8 @@ name: Various rustc tests on: - push +permissions: {} + jobs: bootstrap_rustc: runs-on: ubuntu-latest diff --git a/compiler/rustc_codegen_cranelift/Readme.md b/compiler/rustc_codegen_cranelift/Readme.md index a297b22326f37..00ea15cb38cc2 100644 --- a/compiler/rustc_codegen_cranelift/Readme.md +++ b/compiler/rustc_codegen_cranelift/Readme.md @@ -101,27 +101,7 @@ For additional ways to use rustc_codegen_cranelift like the JIT mode see [usage. ## Building and testing with changes in rustc code -This is useful when changing code in `rustc_codegen_cranelift` as part of changing [main Rust repository](https://github.com/rust-lang/rust/). -This can happen, for example, when you are implementing a new compiler intrinsic. - -Instruction below uses `$RustCheckoutDir` as substitute for any folder where you cloned Rust repository. - -You need to do this steps to successfully compile and use the cranelift backend with your changes in rustc code: - -1. `cd $RustCheckoutDir` -2. Run `python x.py setup` and choose option for compiler (`b`). -3. Build compiler and necessary tools: `python x.py build --stage=2 compiler library/std src/tools/rustdoc src/tools/rustfmt` - * (Optional) You can also build cargo by adding `src/tools/cargo` to previous command. -4. Copy cargo from a nightly toolchain: `cp $(rustup +nightly which cargo) ./build/host/stage2/bin/cargo`. Note that you would need to do this every time you rebuilt `rust` repository. -5. Link your new `rustc` to toolchain: `rustup toolchain link stage2 ./build/host/stage2/`. -6. (Windows only) compile the build system: `rustc +stage2 -O build_system/main.rs -o y.exe`. -7. You need to prefix every `./y.sh` (or `y` if you built `build_system/main.rs` as `y`) command by `rustup run stage2` to make cg_clif use your local changes in rustc. - * `rustup run stage2 ./y.sh prepare` - * `rustup run stage2 ./y.sh build` - * (Optional) run tests: `rustup run stage2 ./y.sh test` -8. Now you can use your cg_clif build to compile other Rust programs, e.g. you can open any Rust crate and run commands like `$RustCheckoutDir/compiler/rustc_codegen_cranelift/dist/cargo-clif build --release`. - -You can also set `rust-analyzer.rustc.source` to your rust workspace to get rust-analyzer to understand your changes. +See [rustc_testing.md](docs/rustc_testing.md). ## Not yet supported diff --git a/compiler/rustc_codegen_cranelift/build_system/build_backend.rs b/compiler/rustc_codegen_cranelift/build_system/build_backend.rs index d90111adf7761..129713e574ad0 100644 --- a/compiler/rustc_codegen_cranelift/build_system/build_backend.rs +++ b/compiler/rustc_codegen_cranelift/build_system/build_backend.rs @@ -1,9 +1,10 @@ +use std::env; use std::path::PathBuf; use crate::path::{Dirs, RelPath}; use crate::rustc_info::get_file_name; use crate::shared_utils::{rustflags_from_env, rustflags_to_cmd_env}; -use crate::utils::{is_ci, is_ci_opt, maybe_incremental, CargoProject, Compiler, LogGroup}; +use crate::utils::{CargoProject, Compiler, LogGroup}; pub(crate) static CG_CLIF: CargoProject = CargoProject::new(&RelPath::SOURCE, "cg_clif"); @@ -16,20 +17,15 @@ pub(crate) fn build_backend( let _group = LogGroup::guard("Build backend"); let mut cmd = CG_CLIF.build(&bootstrap_host_compiler, dirs); - maybe_incremental(&mut cmd); let mut rustflags = rustflags_from_env("RUSTFLAGS"); rustflags.push("-Zallow-features=rustc_private".to_owned()); - if is_ci() { - // Deny warnings on CI - rustflags.push("-Dwarnings".to_owned()); - - if !is_ci_opt() { - cmd.env("CARGO_PROFILE_RELEASE_DEBUG_ASSERTIONS", "true"); - cmd.env("CARGO_PROFILE_RELEASE_OVERFLOW_CHECKS", "true"); - } + if env::var("CG_CLIF_EXPENSIVE_CHECKS").is_ok() { + // Enabling debug assertions implicitly enables the clif ir verifier + cmd.env("CARGO_PROFILE_RELEASE_DEBUG_ASSERTIONS", "true"); + cmd.env("CARGO_PROFILE_RELEASE_OVERFLOW_CHECKS", "true"); } if use_unstable_features { diff --git a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs index 1ed896c6bf0e4..10c3f9cfa2ce3 100644 --- a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs +++ b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs @@ -6,8 +6,7 @@ use std::process::Command; use crate::path::{Dirs, RelPath}; use crate::rustc_info::get_file_name; use crate::utils::{ - maybe_incremental, remove_dir_if_exists, spawn_and_wait, try_hard_link, CargoProject, Compiler, - LogGroup, + remove_dir_if_exists, spawn_and_wait, try_hard_link, CargoProject, Compiler, LogGroup, }; use crate::{config, CodegenBackend, SysrootKind}; @@ -270,7 +269,6 @@ fn build_clif_sysroot_for_triple( } compiler.rustflags.extend(rustflags); let mut build_cmd = STANDARD_LIBRARY.build(&compiler, dirs); - maybe_incremental(&mut build_cmd); if channel == "release" { build_cmd.arg("--release"); } diff --git a/compiler/rustc_codegen_cranelift/build_system/main.rs b/compiler/rustc_codegen_cranelift/build_system/main.rs index e8cf486e966ed..cdd2bae03f8f1 100644 --- a/compiler/rustc_codegen_cranelift/build_system/main.rs +++ b/compiler/rustc_codegen_cranelift/build_system/main.rs @@ -6,7 +6,7 @@ use std::env; use std::path::PathBuf; use std::process; -use self::utils::{is_ci, is_ci_opt, Compiler}; +use self::utils::Compiler; mod abi_cafe; mod bench; @@ -60,14 +60,9 @@ fn main() { } env::set_var("CG_CLIF_DISABLE_INCR_CACHE", "1"); - if is_ci() { - // Disabling incr comp reduces cache size and incr comp doesn't save as much on CI anyway - env::set_var("CARGO_BUILD_INCREMENTAL", "false"); - - if !is_ci_opt() { - // Enable the Cranelift verifier - env::set_var("CG_CLIF_ENABLE_VERIFIER", "1"); - } + // Force incr comp even in release mode unless in CI or incremental builds are explicitly disabled + if env::var_os("CARGO_BUILD_INCREMENTAL").is_none() { + env::set_var("CARGO_BUILD_INCREMENTAL", "true"); } let mut args = env::args().skip(1); diff --git a/compiler/rustc_codegen_cranelift/build_system/prepare.rs b/compiler/rustc_codegen_cranelift/build_system/prepare.rs index 3677d0a7d3607..5525a5f63e93b 100644 --- a/compiler/rustc_codegen_cranelift/build_system/prepare.rs +++ b/compiler/rustc_codegen_cranelift/build_system/prepare.rs @@ -15,7 +15,6 @@ pub(crate) fn prepare(dirs: &Dirs) { RelPath::DOWNLOAD.ensure_exists(dirs); crate::tests::RAND_REPO.fetch(dirs); crate::tests::REGEX_REPO.fetch(dirs); - crate::tests::PORTABLE_SIMD_REPO.fetch(dirs); } pub(crate) fn prepare_stdlib(dirs: &Dirs, rustc: &Path) { diff --git a/compiler/rustc_codegen_cranelift/build_system/tests.rs b/compiler/rustc_codegen_cranelift/build_system/tests.rs index 1c3e615c7aba2..9efb6ed715ca7 100644 --- a/compiler/rustc_codegen_cranelift/build_system/tests.rs +++ b/compiler/rustc_codegen_cranelift/build_system/tests.rs @@ -130,16 +130,10 @@ pub(crate) static REGEX_REPO: GitRepo = GitRepo::github( pub(crate) static REGEX: CargoProject = CargoProject::new(®EX_REPO.source_dir(), "regex_target"); -pub(crate) static PORTABLE_SIMD_REPO: GitRepo = GitRepo::github( - "rust-lang", - "portable-simd", - "5794c837bc605c4cd9dbb884285976dfdb293cce", - "a64d8fdd0ed0d9c4", - "portable-simd", -); +pub(crate) static PORTABLE_SIMD_SRC: RelPath = RelPath::BUILD.join("coretests"); pub(crate) static PORTABLE_SIMD: CargoProject = - CargoProject::new(&PORTABLE_SIMD_REPO.source_dir(), "portable-simd_target"); + CargoProject::new(&PORTABLE_SIMD_SRC, "portable-simd_target"); static LIBCORE_TESTS_SRC: RelPath = RelPath::BUILD.join("coretests"); @@ -221,7 +215,12 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[ } }), TestCase::custom("test.portable-simd", &|runner| { - PORTABLE_SIMD_REPO.patch(&runner.dirs); + apply_patches( + &runner.dirs, + "portable-simd", + &runner.stdlib_source.join("library/portable-simd"), + &PORTABLE_SIMD_SRC.to_path(&runner.dirs), + ); PORTABLE_SIMD.clean(&runner.dirs); diff --git a/compiler/rustc_codegen_cranelift/build_system/utils.rs b/compiler/rustc_codegen_cranelift/build_system/utils.rs index 149f1618f5c0e..9f95122b341c5 100644 --- a/compiler/rustc_codegen_cranelift/build_system/utils.rs +++ b/compiler/rustc_codegen_cranelift/build_system/utils.rs @@ -254,14 +254,6 @@ pub(crate) fn copy_dir_recursively(from: &Path, to: &Path) { } } -pub(crate) fn is_ci() -> bool { - env::var("CI").is_ok() -} - -pub(crate) fn is_ci_opt() -> bool { - env::var("CI_OPT").is_ok() -} - static IN_GROUP: AtomicBool = AtomicBool::new(false); pub(crate) struct LogGroup { is_gha: bool, @@ -288,13 +280,3 @@ impl Drop for LogGroup { IN_GROUP.store(false, Ordering::SeqCst); } } - -pub(crate) fn maybe_incremental(cmd: &mut Command) { - if is_ci() || std::env::var("CARGO_BUILD_INCREMENTAL").map_or(false, |val| val == "false") { - // Disabling incr comp reduces cache size and incr comp doesn't save as much on CI anyway - cmd.env("CARGO_BUILD_INCREMENTAL", "false"); - } else { - // Force incr comp even in release mode unless in CI or incremental builds are explicitly disabled - cmd.env("CARGO_BUILD_INCREMENTAL", "true"); - } -} diff --git a/compiler/rustc_codegen_cranelift/docs/rustc_testing.md b/compiler/rustc_codegen_cranelift/docs/rustc_testing.md new file mode 100644 index 0000000000000..88c555572318b --- /dev/null +++ b/compiler/rustc_codegen_cranelift/docs/rustc_testing.md @@ -0,0 +1,23 @@ +# Building and testing with changes in rustc code + +This is useful when changing code in `rustc_codegen_cranelift` as part of changing [main Rust repository](https://github.com/rust-lang/rust/). +This can happen, for example, when you are implementing a new compiler intrinsic. + +Instruction below uses `$RustCheckoutDir` as substitute for any folder where you cloned Rust repository. + +You need to do this steps to successfully compile and use the cranelift backend with your changes in rustc code: + +1. `cd $RustCheckoutDir` +2. Run `python x.py setup` and choose option for compiler (`b`). +3. Build compiler and necessary tools: `python x.py build --stage=2 compiler library/std src/tools/rustdoc src/tools/rustfmt` + * (Optional) You can also build cargo by adding `src/tools/cargo` to previous command. +4. Copy cargo from a nightly toolchain: `cp $(rustup +nightly which cargo) ./build/host/stage2/bin/cargo`. Note that you would need to do this every time you rebuilt `rust` repository. +5. Link your new `rustc` to toolchain: `rustup toolchain link stage2 ./build/host/stage2/`. +6. (Windows only) compile the build system: `rustc +stage2 -O build_system/main.rs -o y.exe`. +7. You need to prefix every `./y.sh` (or `y` if you built `build_system/main.rs` as `y`) command by `rustup run stage2` to make cg_clif use your local changes in rustc. + * `rustup run stage2 ./y.sh prepare` + * `rustup run stage2 ./y.sh build` + * (Optional) run tests: `rustup run stage2 ./y.sh test` +8. Now you can use your cg_clif build to compile other Rust programs, e.g. you can open any Rust crate and run commands like `$RustCheckoutDir/compiler/rustc_codegen_cranelift/dist/cargo-clif build --release`. + +You can also set `rust-analyzer.rustc.source` to your rust workspace to get rust-analyzer to understand your changes. diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs index 39988cf64e54b..e45c16ee280a7 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs @@ -90,8 +90,9 @@ unsafe impl Sync for i16 {} unsafe impl Sync for i32 {} unsafe impl Sync for isize {} unsafe impl Sync for char {} +unsafe impl Sync for f32 {} unsafe impl<'a, T: ?Sized> Sync for &'a T {} -unsafe impl Sync for [u8; 16] {} +unsafe impl Sync for [T; N] {} #[lang = "freeze"] unsafe auto trait Freeze {} @@ -465,6 +466,35 @@ pub fn panic(_msg: &'static str) -> ! { } } +macro_rules! panic_const { + ($($lang:ident = $message:expr,)+) => { + pub mod panic_const { + use super::*; + + $( + #[track_caller] + #[lang = stringify!($lang)] + pub fn $lang() -> ! { + panic($message); + } + )+ + } + } +} + +panic_const! { + panic_const_add_overflow = "attempt to add with overflow", + panic_const_sub_overflow = "attempt to subtract with overflow", + panic_const_mul_overflow = "attempt to multiply with overflow", + panic_const_div_overflow = "attempt to divide with overflow", + panic_const_rem_overflow = "attempt to calculate the remainder with overflow", + panic_const_neg_overflow = "attempt to negate with overflow", + panic_const_shr_overflow = "attempt to shift right with overflow", + panic_const_shl_overflow = "attempt to shift left with overflow", + panic_const_div_by_zero = "attempt to divide by zero", + panic_const_rem_by_zero = "attempt to calculate the remainder with a divisor of zero", +} + #[lang = "panic_bounds_check"] #[track_caller] fn panic_bounds_check(index: usize, len: usize) -> ! { diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain index 612fc61ec27df..09e436b3eed0f 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain +++ b/compiler/rustc_codegen_cranelift/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2024-03-28" +channel = "nightly-2024-04-05" components = ["rust-src", "rustc-dev", "llvm-tools"] diff --git a/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs b/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs index 03912b18ea5f2..0252d5b334036 100755 --- a/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs +++ b/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs @@ -1,5 +1,5 @@ #!/usr/bin/env bash -#![forbid(unsafe_code)]/* This line is ignored by bash +#![rustfmt::skip]/* This line is ignored by bash # This block is ignored by rustc pushd $(dirname "$0")/../ RUSTC="$(pwd)/dist/rustc-clif" @@ -26,11 +26,8 @@ fn main() -> Result<(), Box> { } let profile = std::fs::read_to_string(profile_name) .map_err(|err| format!("Failed to read profile {}", err))?; - let mut output = std::fs::OpenOptions::new() - .create(true) - .write(true) - .truncate(true) - .open(output_name)?; + let mut output = + std::fs::OpenOptions::new().create(true).write(true).truncate(true).open(output_name)?; for line in profile.lines() { let mut stack = &line[..line.rfind(" ").unwrap()]; diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index b4ea4e10a3d0f..0aa2bae8f78b1 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -372,8 +372,14 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { ); } _ => { - let msg_str = msg.description(); - codegen_panic(fx, msg_str, source_info); + let location = fx.get_caller_location(source_info).load_scalar(fx); + + codegen_panic_inner( + fx, + msg.panic_function(), + &[location], + Some(source_info.span), + ); } } } @@ -643,8 +649,8 @@ fn codegen_stmt<'tcx>( | CastKind::IntToFloat | CastKind::FnPtrToPtr | CastKind::PtrToPtr - | CastKind::PointerExposeAddress - | CastKind::PointerFromExposedAddress, + | CastKind::PointerExposeProvenance + | CastKind::PointerWithExposedProvenance, ref operand, to_ty, ) => { @@ -957,20 +963,6 @@ pub(crate) fn codegen_operand<'tcx>( } } -pub(crate) fn codegen_panic<'tcx>( - fx: &mut FunctionCx<'_, '_, 'tcx>, - msg_str: &str, - source_info: mir::SourceInfo, -) { - let location = fx.get_caller_location(source_info).load_scalar(fx); - - let msg_ptr = fx.anonymous_str(msg_str); - let msg_len = fx.bcx.ins().iconst(fx.pointer_type, i64::try_from(msg_str.len()).unwrap()); - let args = [msg_ptr, msg_len, location]; - - codegen_panic_inner(fx, rustc_hir::LangItem::Panic, &args, Some(source_info.span)); -} - pub(crate) fn codegen_panic_nounwind<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, msg_str: &str, diff --git a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs index b2bc289a5b6ba..4a5ef352151f3 100644 --- a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs +++ b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs @@ -68,7 +68,7 @@ pub(crate) fn maybe_codegen<'tcx>( Some(CValue::by_val(ret_val, lhs.layout())) } } - BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne => None, + BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne | BinOp::Cmp => None, BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => None, } } @@ -134,6 +134,7 @@ pub(crate) fn maybe_codegen_checked<'tcx>( BinOp::AddUnchecked | BinOp::SubUnchecked | BinOp::MulUnchecked => 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!(), } diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs index 380eba437c270..32b9c824ded2f 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs @@ -89,11 +89,7 @@ impl DebugContext { match &source_file.name { FileName::Real(path) => { let (dir_path, file_name) = - split_path_dir_and_file(if self.should_remap_filepaths { - path.remapped_path_if_available() - } else { - path.local_path_if_available() - }); + split_path_dir_and_file(path.to_path(self.filename_display_preference)); let dir_name = osstr_as_utf8_bytes(dir_path.as_os_str()); let file_name = osstr_as_utf8_bytes(file_name); @@ -115,14 +111,7 @@ impl DebugContext { filename => { let dir_id = line_program.default_directory(); let dummy_file_name = LineString::new( - filename - .display(if self.should_remap_filepaths { - FileNameDisplayPreference::Remapped - } else { - FileNameDisplayPreference::Local - }) - .to_string() - .into_bytes(), + filename.display(self.filename_display_preference).to_string().into_bytes(), line_program.encoding(), line_strings, ); diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs index 1bb0e59051372..5d943b5d99657 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs @@ -42,7 +42,7 @@ pub(crate) struct DebugContext { namespace_map: DefIdMap, array_size_type: UnitEntryId, - should_remap_filepaths: bool, + filename_display_preference: FileNameDisplayPreference, } pub(crate) struct FunctionDebugContext { @@ -84,22 +84,18 @@ impl DebugContext { let mut dwarf = DwarfUnit::new(encoding); - let should_remap_filepaths = tcx.sess.should_prefer_remapped_for_codegen(); + use rustc_session::config::RemapPathScopeComponents; + + let filename_display_preference = + tcx.sess.filename_display_preference(RemapPathScopeComponents::DEBUGINFO); let producer = producer(tcx.sess); - let comp_dir = tcx - .sess - .opts - .working_dir - .to_string_lossy(if should_remap_filepaths { - FileNameDisplayPreference::Remapped - } else { - FileNameDisplayPreference::Local - }) - .into_owned(); + let comp_dir = + tcx.sess.opts.working_dir.to_string_lossy(filename_display_preference).to_string(); + let (name, file_info) = match tcx.sess.local_crate_source_file() { Some(path) => { - let name = path.to_string_lossy().into_owned(); + let name = path.to_string_lossy(filename_display_preference).to_string(); (name, None) } None => (tcx.crate_name(LOCAL_CRATE).to_string(), None), @@ -156,7 +152,7 @@ impl DebugContext { stack_pointer_register, namespace_map: DefIdMap::default(), array_size_type, - should_remap_filepaths, + filename_display_preference, } } diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs index 1615dc5de697b..8df83c706a100 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs @@ -1393,7 +1393,7 @@ fn llvm_add_sub<'tcx>( // c + carry -> c + first intermediate carry or borrow respectively let int0 = crate::num::codegen_checked_int_binop(fx, bin_op, a, b); - let c = int0.value_field(fx, FieldIdx::new(0)); + let c = int0.value_field(fx, FieldIdx::ZERO); let cb0 = int0.value_field(fx, FieldIdx::new(1)).load_scalar(fx); // c + carry -> c + second intermediate carry or borrow respectively diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs index 4d55a95aa9db7..67f9d83106294 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs @@ -965,7 +965,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( }); } - sym::simd_expose_addr | sym::simd_from_exposed_addr | sym::simd_cast_ptr => { + sym::simd_expose_provenance | sym::simd_with_exposed_provenance | sym::simd_cast_ptr => { intrinsic_args!(fx, args => (arg); intrinsic); ret.write_cvalue_transmute(fx, arg); } diff --git a/compiler/rustc_codegen_cranelift/src/num.rs b/compiler/rustc_codegen_cranelift/src/num.rs index 8992f40fb903a..714858084ec9d 100644 --- a/compiler/rustc_codegen_cranelift/src/num.rs +++ b/compiler/rustc_codegen_cranelift/src/num.rs @@ -40,6 +40,22 @@ pub(crate) fn bin_op_to_intcc(bin_op: BinOp, signed: bool) -> Option { }) } +fn codegen_three_way_compare<'tcx>( + fx: &mut FunctionCx<'_, '_, 'tcx>, + signed: bool, + lhs: Value, + rhs: Value, +) -> 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 = 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); + CValue::by_val(val, fx.layout_of(fx.tcx.ty_ordering_enum(Some(fx.mir.span)))) +} + fn codegen_compare_bin_op<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, bin_op: BinOp, @@ -47,6 +63,10 @@ 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 val = fx.bcx.ins().icmp(intcc, lhs, rhs); CValue::by_val(val, fx.layout_of(fx.tcx.types.bool)) @@ -59,7 +79,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::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt | BinOp::Cmp => { match in_lhs.layout().ty.kind() { ty::Bool | ty::Uint(_) | ty::Int(_) | ty::Char => { let signed = type_sign(in_lhs.layout().ty); @@ -110,7 +130,7 @@ pub(crate) fn codegen_int_binop<'tcx>( in_lhs: CValue<'tcx>, in_rhs: CValue<'tcx>, ) -> CValue<'tcx> { - if bin_op != BinOp::Shl && bin_op != BinOp::Shr { + if !matches!(bin_op, BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked) { assert_eq!( in_lhs.layout().ty, in_rhs.layout().ty, @@ -160,7 +180,7 @@ pub(crate) fn codegen_int_binop<'tcx>( } BinOp::Offset => unreachable!("Offset is not an integer operation"), // Compare binops handles by `codegen_binop`. - BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge => { + BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge | BinOp::Cmp => { unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs.layout().ty, in_rhs.layout().ty); } }; diff --git a/compiler/rustc_codegen_cranelift/src/vtable.rs b/compiler/rustc_codegen_cranelift/src/vtable.rs index 86ebf37d105f6..04e24320f9131 100644 --- a/compiler/rustc_codegen_cranelift/src/vtable.rs +++ b/compiler/rustc_codegen_cranelift/src/vtable.rs @@ -61,7 +61,7 @@ pub(crate) fn get_ptr_and_method_ref<'tcx>( if ty.is_dyn_star() { let inner_layout = fx.layout_of(arg.layout().ty.builtin_deref(true).unwrap().ty); let dyn_star = CPlace::for_ptr(Pointer::new(arg.load_scalar(fx)), inner_layout); - let ptr = dyn_star.place_field(fx, FieldIdx::new(0)).to_ptr(); + let ptr = dyn_star.place_field(fx, FieldIdx::ZERO).to_ptr(); let vtable = dyn_star.place_field(fx, FieldIdx::new(1)).to_cvalue(fx).load_scalar(fx); break 'block (ptr, vtable); diff --git a/compiler/rustc_codegen_gcc/Cargo.lock b/compiler/rustc_codegen_gcc/Cargo.lock index ab2c7ca8a47c8..3ecb0ef6b4d29 100644 --- a/compiler/rustc_codegen_gcc/Cargo.lock +++ b/compiler/rustc_codegen_gcc/Cargo.lock @@ -79,16 +79,18 @@ dependencies = [ [[package]] name = "gccjit" -version = "1.0.0" -source = "git+https://github.com/antoyo/gccjit.rs#9f8f67edc006d543b17529a001803ffece48349e" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecaa4c3da2d74c1a991b4faff75d49ab1d0522d9a99d8e2614b3b04d226417ce" dependencies = [ "gccjit_sys", ] [[package]] name = "gccjit_sys" -version = "0.0.1" -source = "git+https://github.com/antoyo/gccjit.rs#9f8f67edc006d543b17529a001803ffece48349e" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "406a66fba005f1a02661f2f9443e5693dd3a667b7c58e70aa4ccc4c8b50b4758" dependencies = [ "libc", ] diff --git a/compiler/rustc_codegen_gcc/Cargo.toml b/compiler/rustc_codegen_gcc/Cargo.toml index 100c10ef1d7af..c5aa2eed1e004 100644 --- a/compiler/rustc_codegen_gcc/Cargo.toml +++ b/compiler/rustc_codegen_gcc/Cargo.toml @@ -22,7 +22,7 @@ master = ["gccjit/master"] default = ["master"] [dependencies] -gccjit = { git = "https://github.com/antoyo/gccjit.rs" } +gccjit = "2.0" # Local copy. #gccjit = { path = "../gccjit.rs" } diff --git a/compiler/rustc_codegen_gcc/example/mini_core.rs b/compiler/rustc_codegen_gcc/example/mini_core.rs index a868471ed1d4d..4665009e191e6 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core.rs @@ -418,6 +418,36 @@ pub fn panic(_msg: &'static str) -> ! { } } +macro_rules! panic_const { + ($($lang:ident = $message:expr,)+) => { + #[cfg(not(bootstrap))] + pub mod panic_const { + use super::*; + + $( + #[track_caller] + #[lang = stringify!($lang)] + pub fn $lang() -> ! { + panic($message); + } + )+ + } + } +} + +panic_const! { + panic_const_add_overflow = "attempt to add with overflow", + panic_const_sub_overflow = "attempt to subtract with overflow", + panic_const_mul_overflow = "attempt to multiply with overflow", + panic_const_div_overflow = "attempt to divide with overflow", + panic_const_rem_overflow = "attempt to calculate the remainder with overflow", + panic_const_neg_overflow = "attempt to negate with overflow", + panic_const_shr_overflow = "attempt to shift right with overflow", + panic_const_shl_overflow = "attempt to shift left with overflow", + panic_const_div_by_zero = "attempt to divide by zero", + panic_const_rem_by_zero = "attempt to calculate the remainder with a divisor of zero", +} + #[lang = "panic_cannot_unwind"] fn panic_cannot_unwind() -> ! { unsafe { diff --git a/compiler/rustc_codegen_gcc/patches/libgccjit12/0001-core-Disable-portable-simd-test.patch b/compiler/rustc_codegen_gcc/patches/libgccjit12/0001-core-Disable-portable-simd-test.patch index 914ae986b50e7..36d0789d2a23b 100644 --- a/compiler/rustc_codegen_gcc/patches/libgccjit12/0001-core-Disable-portable-simd-test.patch +++ b/compiler/rustc_codegen_gcc/patches/libgccjit12/0001-core-Disable-portable-simd-test.patch @@ -14,7 +14,7 @@ index d0a119c..76fdece 100644 @@ -89,7 +89,6 @@ #![feature(never_type)] #![feature(unwrap_infallible)] - #![feature(pointer_is_aligned)] + #![feature(pointer_is_aligned_to)] -#![feature(portable_simd)] #![feature(ptr_metadata)] #![feature(lazy_cell)] @@ -27,6 +27,6 @@ index d0a119c..76fdece 100644 mod slice; mod str; mod str_lossy; --- +-- 2.42.1 diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index d243d7088ada9..78d943192db07 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -94,6 +94,10 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> { self.const_int(self.type_i32(), i as i64) } + fn const_i8(&self, i: i8) -> RValue<'gcc> { + self.const_int(self.type_i8(), i as i64) + } + fn const_u32(&self, i: u32) -> RValue<'gcc> { self.const_uint(self.type_u32(), i as u64) } diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index e5f5146fac8fb..d2828669d438f 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -16,13 +16,15 @@ pub use rustc_middle::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA}; use rustc_middle::ty::Ty; use rustc_session::config; pub use rustc_target::abi::call::*; -use rustc_target::abi::{self, HasDataLayout, Int}; +use rustc_target::abi::{self, HasDataLayout, Int, Size}; pub use rustc_target::spec::abi::Abi; use rustc_target::spec::SanitizerSet; use libc::c_uint; use smallvec::SmallVec; +use std::cmp; + pub trait ArgAttributesExt { fn apply_attrs_to_llfn(&self, idx: AttributePlace, cx: &CodegenCx<'_, '_>, llfn: &Value); fn apply_attrs_to_callsite( @@ -130,42 +132,36 @@ impl LlvmType for Reg { impl LlvmType for CastTarget { fn llvm_type<'ll>(&self, cx: &CodegenCx<'ll, '_>) -> &'ll Type { let rest_ll_unit = self.rest.unit.llvm_type(cx); - let (rest_count, rem_bytes) = if self.rest.unit.size.bytes() == 0 { - (0, 0) + let rest_count = if self.rest.total == Size::ZERO { + 0 } else { - ( - self.rest.total.bytes() / self.rest.unit.size.bytes(), - self.rest.total.bytes() % self.rest.unit.size.bytes(), - ) + assert_ne!( + self.rest.unit.size, + Size::ZERO, + "total size {:?} cannot be divided into units of zero size", + self.rest.total + ); + if self.rest.total.bytes() % self.rest.unit.size.bytes() != 0 { + assert_eq!(self.rest.unit.kind, RegKind::Integer, "only int regs can be split"); + } + self.rest.total.bytes().div_ceil(self.rest.unit.size.bytes()) }; + // Simplify to a single unit or an array if there's no prefix. + // This produces the same layout, but using a simpler type. if self.prefix.iter().all(|x| x.is_none()) { - // Simplify to a single unit when there is no prefix and size <= unit size - if self.rest.total <= self.rest.unit.size { + if rest_count == 1 { return rest_ll_unit; } - // Simplify to array when all chunks are the same size and type - if rem_bytes == 0 { - return cx.type_array(rest_ll_unit, rest_count); - } - } - - // Create list of fields in the main structure - let mut args: Vec<_> = self - .prefix - .iter() - .flat_map(|option_reg| option_reg.map(|reg| reg.llvm_type(cx))) - .chain((0..rest_count).map(|_| rest_ll_unit)) - .collect(); - - // Append final integer - if rem_bytes != 0 { - // Only integers can be really split further. - assert_eq!(self.rest.unit.kind, RegKind::Integer); - args.push(cx.type_ix(rem_bytes * 8)); + return cx.type_array(rest_ll_unit, rest_count); } + // Generate a struct type with the prefix and the "rest" arguments. + let prefix_args = + self.prefix.iter().flat_map(|option_reg| option_reg.map(|reg| reg.llvm_type(cx))); + let rest_args = (0..rest_count).map(|_| rest_ll_unit); + let args: Vec<_> = prefix_args.chain(rest_args).collect(); cx.type_struct(&args, false) } } @@ -215,47 +211,33 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { bug!("unsized `ArgAbi` must be handled through `store_fn_arg`"); } PassMode::Cast { cast, pad_i32: _ } => { - // FIXME(eddyb): Figure out when the simpler Store is safe, clang - // uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}. - let can_store_through_cast_ptr = false; - if can_store_through_cast_ptr { - bx.store(val, dst.llval, self.layout.align.abi); - } else { - // The actual return type is a struct, but the ABI - // adaptation code has cast it into some scalar type. The - // code that follows is the only reliable way I have - // found to do a transform like i64 -> {i32,i32}. - // Basically we dump the data onto the stack then memcpy it. - // - // Other approaches I tried: - // - Casting rust ret pointer to the foreign type and using Store - // is (a) unsafe if size of foreign type > size of rust type and - // (b) runs afoul of strict aliasing rules, yielding invalid - // assembly under -O (specifically, the store gets removed). - // - Truncating foreign type to correct integral type and then - // bitcasting to the struct type yields invalid cast errors. - - // We instead thus allocate some scratch space... - let scratch_size = cast.size(bx); - let scratch_align = cast.align(bx); - let llscratch = bx.alloca(cast.llvm_type(bx), scratch_align); - bx.lifetime_start(llscratch, scratch_size); - - // ... where we first store the value... - bx.store(val, llscratch, scratch_align); - - // ... and then memcpy it to the intended destination. - bx.memcpy( - dst.llval, - self.layout.align.abi, - llscratch, - scratch_align, - bx.const_usize(self.layout.size.bytes()), - MemFlags::empty(), - ); - - bx.lifetime_end(llscratch, scratch_size); - } + // The ABI mandates that the value is passed as a different struct representation. + // Spill and reload it from the stack to convert from the ABI representation to + // the Rust representation. + let scratch_size = cast.size(bx); + let scratch_align = cast.align(bx); + // Note that the ABI type may be either larger or smaller than the Rust type, + // due to the presence or absence of trailing padding. For example: + // - On some ABIs, the Rust layout { f64, f32, } may omit padding + // when passed by value, making it smaller. + // - On some ABIs, the Rust layout { u16, u16, u16 } may be padded up to 8 bytes + // when passed by value, making it larger. + let copy_bytes = cmp::min(scratch_size.bytes(), self.layout.size.bytes()); + // Allocate some scratch space... + let llscratch = bx.alloca(cast.llvm_type(bx), scratch_align); + bx.lifetime_start(llscratch, scratch_size); + // ...store the value... + bx.store(val, llscratch, scratch_align); + // ... and then memcpy it to the intended destination. + bx.memcpy( + dst.llval, + self.layout.align.abi, + llscratch, + scratch_align, + bx.const_usize(copy_bytes), + MemFlags::empty(), + ); + bx.lifetime_end(llscratch, scratch_size); } _ => { OperandRef::from_immediate_or_packed_pair(bx, val, self.layout).val.store(bx, dst); diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index f9eaa0d94cbbd..870e5ab329619 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -417,7 +417,7 @@ pub fn from_fn_attrs<'ll, 'tcx>( to_add.push(llvm::CreateAttrString(cx.llcx, "cmse_nonsecure_entry")); } if let Some(align) = codegen_fn_attrs.alignment { - llvm::set_alignment(llfn, align as usize); + llvm::set_alignment(llfn, align); } to_add.extend(sanitize_attrs(cx, codegen_fn_attrs.no_sanitize)); diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 031bbd6336116..4efea66a7f1e1 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -29,7 +29,8 @@ use rustc_data_structures::small_c_str::SmallCStr; use rustc_errors::{DiagCtxt, FatalError, Level}; use rustc_fs_util::{link_or_copy, path_to_c_string}; use rustc_middle::ty::TyCtxt; -use rustc_session::config::{self, Lto, OutputType, Passes, SplitDwarfKind, SwitchWithOptPath}; +use rustc_session::config::{self, Lto, OutputType, Passes}; +use rustc_session::config::{RemapPathScopeComponents, SplitDwarfKind, SwitchWithOptPath}; use rustc_session::Session; use rustc_span::symbol::sym; use rustc_span::InnerSpan; @@ -257,18 +258,17 @@ pub fn target_machine_factory( }; let debuginfo_compression = SmallCStr::new(&debuginfo_compression); - let should_prefer_remapped_for_split_debuginfo_paths = - sess.should_prefer_remapped_for_split_debuginfo_paths(); + let file_name_display_preference = + sess.filename_display_preference(RemapPathScopeComponents::DEBUGINFO); Arc::new(move |config: TargetMachineFactoryConfig| { let path_to_cstring_helper = |path: Option| -> CString { let path = path.unwrap_or_default(); - let path = if should_prefer_remapped_for_split_debuginfo_paths { - path_mapping.map_prefix(path).0 - } else { - path.into() - }; - CString::new(path.to_str().unwrap()).unwrap() + let path = path_mapping + .to_real_filename(path) + .to_string_lossy(file_name_display_preference) + .into_owned(); + CString::new(path).unwrap() }; let split_dwarf_file = path_to_cstring_helper(config.split_dwarf_file); diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 25cbd90460f27..568fcc3f3cf49 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -160,6 +160,10 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { self.const_int(self.type_i32(), i as i64) } + fn const_i8(&self, i: i8) -> &'ll Value { + self.const_int(self.type_i8(), i as i64) + } + fn const_u32(&self, i: u32) -> &'ll Value { self.const_uint(self.type_i32(), i as u64) } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index 0fbc624389bc5..3f3969bbca35c 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -17,11 +17,11 @@ use rustc_span::Symbol; /// Generates and exports the Coverage Map. /// -/// Rust Coverage Map generation supports LLVM Coverage Mapping Format version -/// 6 (zero-based encoded as 5), as defined at -/// [LLVM Code Coverage Mapping Format](https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/docs/CoverageMappingFormat.rst#llvm-code-coverage-mapping-format). +/// Rust Coverage Map generation supports LLVM Coverage Mapping Format versions +/// 6 and 7 (encoded as 5 and 6 respectively), as described at +/// [LLVM Code Coverage Mapping Format](https://github.com/rust-lang/llvm-project/blob/rustc/18.0-2024-02-13/llvm/docs/CoverageMappingFormat.rst). /// These versions are supported by the LLVM coverage tools (`llvm-profdata` and `llvm-cov`) -/// bundled with Rust's fork of LLVM. +/// distributed in the `llvm-tools-preview` rustup component. /// /// Consequently, Rust's bundled version of Clang also generates Coverage Maps compliant with /// the same version. Clang's implementation of Coverage Map generation was referenced when @@ -31,10 +31,21 @@ use rustc_span::Symbol; pub fn finalize(cx: &CodegenCx<'_, '_>) { let tcx = cx.tcx; - // Ensure the installed version of LLVM supports Coverage Map Version 6 - // (encoded as a zero-based value: 5), which was introduced with LLVM 13. - let version = coverageinfo::mapping_version(); - assert_eq!(version, 5, "The `CoverageMappingVersion` exposed by `llvm-wrapper` is out of sync"); + // Ensure that LLVM is using a version of the coverage mapping format that + // agrees with our Rust-side code. Expected versions (encoded as n-1) are: + // - `CovMapVersion::Version6` (5) used by LLVM 13-17 + // - `CovMapVersion::Version7` (6) used by LLVM 18 + let covmap_version = { + let llvm_covmap_version = coverageinfo::mapping_version(); + let expected_versions = 5..=6; + assert!( + expected_versions.contains(&llvm_covmap_version), + "Coverage mapping version exposed by `llvm-wrapper` is out of sync; \ + expected {expected_versions:?} but was {llvm_covmap_version}" + ); + // This is the version number that we will embed in the covmap section: + llvm_covmap_version + }; debug!("Generating coverage map for CodegenUnit: `{}`", cx.codegen_unit.name()); @@ -74,7 +85,7 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) { // Generate the coverage map header, which contains the filenames used by // this CGU's coverage mappings, and store it in a well-known global. - let cov_data_val = generate_coverage_map(cx, version, filenames_size, filenames_val); + let cov_data_val = generate_coverage_map(cx, covmap_version, filenames_size, filenames_val); coverageinfo::save_cov_data_to_mod(cx, cov_data_val); let mut unused_function_names = Vec::new(); @@ -173,8 +184,14 @@ impl GlobalFileTable { // Since rustc generates coverage maps with relative paths, the // compilation directory can be combined with the relative paths // to get absolute paths, if needed. + use rustc_session::config::RemapPathScopeComponents; use rustc_session::RemapFileNameExt; - let working_dir: &str = &tcx.sess.opts.working_dir.for_codegen(tcx.sess).to_string_lossy(); + let working_dir: &str = &tcx + .sess + .opts + .working_dir + .for_scope(tcx.sess, RemapPathScopeComponents::MACRO) + .to_string_lossy(); llvm::build_byte_buffer(|buffer| { coverageinfo::write_filenames_section_to_buffer( diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index 85277db6d538a..140566e8da9bb 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -16,6 +16,7 @@ use rustc_middle::bug; use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::Instance; +use rustc_target::abi::Align; use std::cell::RefCell; @@ -23,8 +24,6 @@ pub(crate) mod ffi; pub(crate) mod map_data; pub mod mapgen; -const VAR_ALIGN_BYTES: usize = 8; - /// A context object for maintaining all state needed by the coverageinfo module. pub struct CrateCoverageContext<'ll, 'tcx> { /// Coverage data for each instrumented function identified by DefId. @@ -225,7 +224,8 @@ pub(crate) fn save_cov_data_to_mod<'ll, 'tcx>( llvm::set_global_constant(llglobal, true); llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage); llvm::set_section(llglobal, &covmap_section_name); - llvm::set_alignment(llglobal, VAR_ALIGN_BYTES); + // LLVM's coverage mapping format specifies 8-byte alignment for items in this section. + llvm::set_alignment(llglobal, Align::EIGHT); cx.add_used_global(llglobal); } @@ -255,7 +255,8 @@ pub(crate) fn save_func_record_to_mod<'ll, 'tcx>( llvm::set_linkage(llglobal, llvm::Linkage::LinkOnceODRLinkage); llvm::set_visibility(llglobal, llvm::Visibility::Hidden); llvm::set_section(llglobal, covfun_section_name); - llvm::set_alignment(llglobal, VAR_ALIGN_BYTES); + // LLVM's coverage mapping format specifies 8-byte alignment for items in this section. + llvm::set_alignment(llglobal, Align::EIGHT); llvm::set_comdat(cx.llmod, llglobal, &func_record_var_name); cx.add_used_global(llglobal); } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 3c76df11e3fc0..e5fecddec528d 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -554,13 +554,16 @@ pub fn file_metadata<'ll>(cx: &CodegenCx<'ll, '_>, source_file: &SourceFile) -> ) -> &'ll DIFile { debug!(?source_file.name); - use rustc_session::RemapFileNameExt; + let filename_display_preference = + cx.sess().filename_display_preference(RemapPathScopeComponents::DEBUGINFO); + + use rustc_session::config::RemapPathScopeComponents; let (directory, file_name) = match &source_file.name { FileName::Real(filename) => { let working_directory = &cx.sess().opts.working_dir; debug!(?working_directory); - if cx.sess().should_prefer_remapped_for_codegen() { + if filename_display_preference == FileNameDisplayPreference::Remapped { let filename = cx .sess() .source_map() @@ -623,7 +626,7 @@ pub fn file_metadata<'ll>(cx: &CodegenCx<'ll, '_>, source_file: &SourceFile) -> } other => { debug!(?other); - ("".into(), other.for_codegen(cx.sess()).to_string_lossy().into_owned()) + ("".into(), other.display(filename_display_preference).to_string()) } }; @@ -832,9 +835,11 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>( codegen_unit_name: &str, debug_context: &CodegenUnitDebugContext<'ll, 'tcx>, ) -> &'ll DIDescriptor { + use rustc_session::{config::RemapPathScopeComponents, RemapFileNameExt}; let mut name_in_debuginfo = tcx .sess .local_crate_source_file() + .map(|src| src.for_scope(&tcx.sess, RemapPathScopeComponents::DEBUGINFO).to_path_buf()) .unwrap_or_else(|| PathBuf::from(tcx.crate_name(LOCAL_CRATE).as_str())); // To avoid breaking split DWARF, we need to ensure that each codegen unit @@ -862,30 +867,29 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>( // FIXME(#41252) Remove "clang LLVM" if we can get GDB and LLVM to play nice. let producer = format!("clang LLVM ({rustc_producer})"); - use rustc_session::RemapFileNameExt; let name_in_debuginfo = name_in_debuginfo.to_string_lossy(); - let work_dir = tcx.sess.opts.working_dir.for_codegen(tcx.sess).to_string_lossy(); + let work_dir = tcx + .sess + .opts + .working_dir + .for_scope(tcx.sess, RemapPathScopeComponents::DEBUGINFO) + .to_string_lossy(); let output_filenames = tcx.output_filenames(()); - let split_name = if tcx.sess.target_can_use_split_dwarf() { - output_filenames - .split_dwarf_path( - tcx.sess.split_debuginfo(), - tcx.sess.opts.unstable_opts.split_dwarf_kind, - Some(codegen_unit_name), - ) - // We get a path relative to the working directory from split_dwarf_path - .map(|f| { - if tcx.sess.should_prefer_remapped_for_split_debuginfo_paths() { - tcx.sess.source_map().path_mapping().map_prefix(f).0 - } else { - f.into() - } - }) + let split_name = if tcx.sess.target_can_use_split_dwarf() + && let Some(f) = output_filenames.split_dwarf_path( + tcx.sess.split_debuginfo(), + tcx.sess.opts.unstable_opts.split_dwarf_kind, + Some(codegen_unit_name), + ) { + // We get a path relative to the working directory from split_dwarf_path + Some(tcx.sess.source_map().path_mapping().to_real_filename(f)) } else { None - } - .unwrap_or_default(); - let split_name = split_name.to_str().unwrap(); + }; + let split_name = split_name + .as_ref() + .map(|f| f.for_scope(tcx.sess, RemapPathScopeComponents::DEBUGINFO).to_string_lossy()) + .unwrap_or_default(); let kind = DebugEmissionKind::from_generic(tcx.sess.opts.debuginfo); let dwarf_version = diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index 1a2498c75a7ff..f58dd4066ad71 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -18,7 +18,9 @@ use crate::llvm; use crate::llvm::AttributePlace::Function; use crate::type_::Type; use crate::value::Value; +use itertools::Itertools; use rustc_codegen_ssa::traits::TypeMembershipMethods; +use rustc_data_structures::fx::FxIndexSet; use rustc_middle::ty::{Instance, Ty}; use rustc_symbol_mangling::typeid::{ kcfi_typeid_for_fnabi, kcfi_typeid_for_instance, typeid_for_fnabi, typeid_for_instance, @@ -141,39 +143,39 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { if self.tcx.sess.is_sanitizer_cfi_enabled() { if let Some(instance) = instance { - let typeid = typeid_for_instance(self.tcx, instance, TypeIdOptions::empty()); - self.set_type_metadata(llfn, typeid); - let typeid = - typeid_for_instance(self.tcx, instance, TypeIdOptions::GENERALIZE_POINTERS); - self.add_type_metadata(llfn, typeid); - let typeid = - typeid_for_instance(self.tcx, instance, TypeIdOptions::NORMALIZE_INTEGERS); - self.add_type_metadata(llfn, typeid); - let typeid = typeid_for_instance( - self.tcx, - instance, - TypeIdOptions::GENERALIZE_POINTERS | TypeIdOptions::NORMALIZE_INTEGERS, - ); - self.add_type_metadata(llfn, typeid); + let mut typeids = FxIndexSet::default(); + for options in [ + TypeIdOptions::GENERALIZE_POINTERS, + TypeIdOptions::NORMALIZE_INTEGERS, + TypeIdOptions::ERASE_SELF_TYPE, + ] + .into_iter() + .powerset() + .map(TypeIdOptions::from_iter) + { + let typeid = typeid_for_instance(self.tcx, instance, options); + if typeids.insert(typeid.clone()) { + self.add_type_metadata(llfn, typeid); + } + } } else { - let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::empty()); - self.set_type_metadata(llfn, typeid); - let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::GENERALIZE_POINTERS); - self.add_type_metadata(llfn, typeid); - let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::NORMALIZE_INTEGERS); - self.add_type_metadata(llfn, typeid); - let typeid = typeid_for_fnabi( - self.tcx, - fn_abi, - TypeIdOptions::GENERALIZE_POINTERS | TypeIdOptions::NORMALIZE_INTEGERS, - ); - self.add_type_metadata(llfn, typeid); + for options in + [TypeIdOptions::GENERALIZE_POINTERS, TypeIdOptions::NORMALIZE_INTEGERS] + .into_iter() + .powerset() + .map(TypeIdOptions::from_iter) + { + let typeid = typeid_for_fnabi(self.tcx, fn_abi, options); + self.add_type_metadata(llfn, typeid); + } } } if self.tcx.sess.is_sanitizer_kcfi_enabled() { // LLVM KCFI does not support multiple !kcfi_type attachments - let mut options = TypeIdOptions::empty(); + // Default to erasing the self type. If we need the concrete type, there will be a + // hint in the instance. + let mut options = TypeIdOptions::ERASE_SELF_TYPE; if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() { options.insert(TypeIdOptions::GENERALIZE_POINTERS); } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index ab135e3ed6444..dc52dd156b7e7 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -2111,7 +2111,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( return Ok(args[0].immediate()); } - if name == sym::simd_expose_addr { + if name == sym::simd_expose_provenance { let (out_len, out_elem) = require_simd!(ret_ty, SimdReturn); require!( in_len == out_len, @@ -2139,7 +2139,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( return Ok(bx.ptrtoint(args[0].immediate(), llret_ty)); } - if name == sym::simd_from_exposed_addr { + if name == sym::simd_with_exposed_provenance { let (out_len, out_elem) = require_simd!(ret_ty, SimdReturn); require!( in_len == out_len, diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 4f5cc575da6e5..6ab1eea959761 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -11,6 +11,7 @@ pub use self::RealPredicate::*; use libc::c_uint; use rustc_data_structures::small_c_str::SmallCStr; use rustc_llvm::RustString; +use rustc_target::abi::Align; use std::cell::RefCell; use std::ffi::{CStr, CString}; use std::str::FromStr; @@ -229,9 +230,9 @@ pub fn set_visibility(llglobal: &Value, visibility: Visibility) { } } -pub fn set_alignment(llglobal: &Value, bytes: usize) { +pub fn set_alignment(llglobal: &Value, align: Align) { unsafe { - ffi::LLVMSetAlignment(llglobal, bytes as c_uint); + ffi::LLVMSetAlignment(llglobal, align.bytes() as c_uint); } } diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index c8b8594c0dd66..7c7f702b2c390 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -2089,14 +2089,14 @@ fn add_rpath_args( .map(|(path, _)| &**path) }) .collect::>(); - let mut rpath_config = RPathConfig { + let rpath_config = RPathConfig { libs: &*libs, out_filename: out_filename.to_path_buf(), has_rpath: sess.target.has_rpath, is_like_osx: sess.target.is_like_osx, linker_is_gnu: sess.target.linker_flavor.is_gnu(), }; - cmd.args(&rpath::get_rpath_flags(&mut rpath_config)); + cmd.args(&rpath::get_rpath_flags(&rpath_config)); } } diff --git a/compiler/rustc_codegen_ssa/src/back/rpath.rs b/compiler/rustc_codegen_ssa/src/back/rpath.rs index 60346228625fa..ebbf49af18487 100644 --- a/compiler/rustc_codegen_ssa/src/back/rpath.rs +++ b/compiler/rustc_codegen_ssa/src/back/rpath.rs @@ -12,7 +12,7 @@ pub struct RPathConfig<'a> { pub linker_is_gnu: bool, } -pub fn get_rpath_flags(config: &mut RPathConfig<'_>) -> Vec { +pub fn get_rpath_flags(config: &RPathConfig<'_>) -> Vec { // No rpath on windows if !config.has_rpath { return Vec::new(); @@ -52,7 +52,7 @@ fn rpaths_to_flags(rpaths: Vec) -> Vec { ret } -fn get_rpaths(config: &mut RPathConfig<'_>) -> Vec { +fn get_rpaths(config: &RPathConfig<'_>) -> Vec { debug!("output: {:?}", config.out_filename.display()); debug!("libs:"); for libpath in config.libs { @@ -73,11 +73,11 @@ fn get_rpaths(config: &mut RPathConfig<'_>) -> Vec { minimize_rpaths(&rpaths) } -fn get_rpaths_relative_to_output(config: &mut RPathConfig<'_>) -> Vec { +fn get_rpaths_relative_to_output(config: &RPathConfig<'_>) -> Vec { config.libs.iter().map(|a| get_rpath_relative_to_output(config, a)).collect() } -fn get_rpath_relative_to_output(config: &mut RPathConfig<'_>, lib: &Path) -> OsString { +fn get_rpath_relative_to_output(config: &RPathConfig<'_>, lib: &Path) -> OsString { // Mac doesn't appear to support $ORIGIN let prefix = if config.is_like_osx { "@loader_path" } else { "$ORIGIN" }; diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index f7f2bfca838ea..410b5d27c5772 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -5,7 +5,7 @@ use crate::back::write::{ compute_per_cgu_lto_type, start_async_codegen, submit_codegened_module_to_llvm, submit_post_lto_module_to_llvm, submit_pre_lto_module_to_llvm, ComputedLtoType, OngoingCodegen, }; -use crate::common::{IntPredicate, RealPredicate, TypeKind}; +use crate::common::{self, IntPredicate, RealPredicate, TypeKind}; use crate::errors; use crate::meth; use crate::mir; @@ -33,7 +33,7 @@ use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem}; use rustc_middle::query::Providers; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; -use rustc_session::config::{self, CrateType, EntryFnType, OutputType}; +use rustc_session::config::{self, CrateType, EntryFnType, OptLevel, OutputType}; use rustc_session::Session; use rustc_span::symbol::sym; use rustc_span::Symbol; @@ -300,14 +300,35 @@ pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } } -pub fn cast_shift_expr_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( +/// Returns `rhs` sufficiently masked, truncated, and/or extended so that +/// it can be used to shift `lhs`. +/// +/// Shifts in MIR are all allowed to have mismatched LHS & RHS types. +/// The shift methods in `BuilderMethods`, however, are fully homogeneous +/// (both parameters and the return type are all the same type). +/// +/// If `is_unchecked` is false, this masks the RHS to ensure it stays in-bounds, +/// as the `BuilderMethods` shifts are UB for out-of-bounds shift amounts. +/// For 32- and 64-bit types, this matches the semantics +/// of Java. (See related discussion on #1877 and #10183.) +/// +/// If `is_unchecked` is true, this does no masking, and adds sufficient `assume` +/// calls or operation flags to preserve as much freedom to optimize as possible. +pub fn build_shift_expr_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, lhs: Bx::Value, - rhs: Bx::Value, + mut rhs: Bx::Value, + is_unchecked: bool, ) -> Bx::Value { // Shifts may have any size int on the rhs let mut rhs_llty = bx.cx().val_ty(rhs); let mut lhs_llty = bx.cx().val_ty(lhs); + + let mask = common::shift_mask_val(bx, lhs_llty, rhs_llty, false); + if !is_unchecked { + rhs = bx.and(rhs, mask); + } + if bx.cx().type_kind(rhs_llty) == TypeKind::Vector { rhs_llty = bx.cx().element_type(rhs_llty) } @@ -317,6 +338,12 @@ pub fn cast_shift_expr_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let rhs_sz = bx.cx().int_width(rhs_llty); let lhs_sz = bx.cx().int_width(lhs_llty); if lhs_sz < rhs_sz { + if is_unchecked && bx.sess().opts.optimize != OptLevel::No { + // FIXME: Use `trunc nuw` once that's available + let inrange = bx.icmp(IntPredicate::IntULE, rhs, mask); + bx.assume(inrange); + } + bx.trunc(rhs, lhs_llty) } else if lhs_sz > rhs_sz { // We zero-extend even if the RHS is signed. So e.g. `(x: i32) << -1i8` will zero-extend the diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index 71fca403defb5..b41739867c7dc 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -3,10 +3,9 @@ use rustc_hir::LangItem; use rustc_middle::mir; use rustc_middle::ty::Instance; -use rustc_middle::ty::{self, layout::TyAndLayout, Ty, TyCtxt}; +use rustc_middle::ty::{self, layout::TyAndLayout, TyCtxt}; use rustc_span::Span; -use crate::base; use crate::traits::*; #[derive(Copy, Clone)] @@ -128,44 +127,6 @@ pub fn build_langcall<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( (bx.fn_abi_of_instance(instance, ty::List::empty()), bx.get_fn_addr(instance), instance) } -// To avoid UB from LLVM, these two functions mask RHS with an -// appropriate mask unconditionally (i.e., the fallback behavior for -// all shifts). For 32- and 64-bit types, this matches the semantics -// of Java. (See related discussion on #1877 and #10183.) - -pub fn build_masked_lshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( - bx: &mut Bx, - lhs: Bx::Value, - rhs: Bx::Value, -) -> Bx::Value { - let rhs = base::cast_shift_expr_rhs(bx, lhs, rhs); - // #1877, #10183: Ensure that input is always valid - let rhs = shift_mask_rhs(bx, rhs); - bx.shl(lhs, rhs) -} - -pub fn build_masked_rshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( - bx: &mut Bx, - lhs_t: Ty<'tcx>, - lhs: Bx::Value, - rhs: Bx::Value, -) -> Bx::Value { - let rhs = base::cast_shift_expr_rhs(bx, lhs, rhs); - // #1877, #10183: Ensure that input is always valid - let rhs = shift_mask_rhs(bx, rhs); - let is_signed = lhs_t.is_signed(); - if is_signed { bx.ashr(lhs, rhs) } else { bx.lshr(lhs, rhs) } -} - -fn shift_mask_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( - bx: &mut Bx, - rhs: Bx::Value, -) -> Bx::Value { - let rhs_llty = bx.val_ty(rhs); - let shift_val = shift_mask_val(bx, rhs_llty, rhs_llty, false); - bx.and(rhs, shift_val) -} - pub fn shift_mask_val<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, llty: Bx::Type, diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 8c668597a43b3..1aa52a985ef7d 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -682,10 +682,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { (LangItem::PanicMisalignedPointerDereference, vec![required, found, location]) } _ => { - let msg = bx.const_str(msg.description()); - // It's `pub fn panic(expr: &str)`, with the wide reference being passed - // as two arguments, and `#[track_caller]` adds an implicit third argument. - (LangItem::Panic, vec![msg.0, msg.1, location]) + // It's `pub fn panic_...()` and `#[track_caller]` adds an implicit argument. + (msg.panic_function(), vec![location]) } }; @@ -1507,9 +1505,35 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if by_ref && !arg.is_indirect() { // Have to load the argument, maybe while casting it. - if let PassMode::Cast { cast: ty, .. } = &arg.mode { - let llty = bx.cast_backend_type(ty); - llval = bx.load(llty, llval, align.min(arg.layout.align.abi)); + if let PassMode::Cast { cast, pad_i32: _ } = &arg.mode { + // The ABI mandates that the value is passed as a different struct representation. + // Spill and reload it from the stack to convert from the Rust representation to + // the ABI representation. + let scratch_size = cast.size(bx); + let scratch_align = cast.align(bx); + // Note that the ABI type may be either larger or smaller than the Rust type, + // due to the presence or absence of trailing padding. For example: + // - On some ABIs, the Rust layout { f64, f32, } may omit padding + // when passed by value, making it smaller. + // - On some ABIs, the Rust layout { u16, u16, u16 } may be padded up to 8 bytes + // when passed by value, making it larger. + let copy_bytes = cmp::min(scratch_size.bytes(), arg.layout.size.bytes()); + // Allocate some scratch space... + let llscratch = bx.alloca(bx.cast_backend_type(cast), scratch_align); + bx.lifetime_start(llscratch, scratch_size); + // ...memcpy the value... + bx.memcpy( + llscratch, + scratch_align, + llval, + align, + bx.const_usize(copy_bytes), + MemFlags::empty(), + ); + // ...and then load it with the ABI type. + let cast_ty = bx.cast_backend_type(cast); + llval = bx.load(cast_ty, llscratch, scratch_align); + bx.lifetime_end(llscratch, scratch_size); } else { // We can't use `PlaceRef::load` here because the argument // may have a type we don't treat as immediate, but the ABI diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 0af84ff067af4..4d746c89f1fc3 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -3,10 +3,11 @@ use super::place::PlaceRef; use super::{FunctionCx, LocalRef}; use crate::base; -use crate::common::{self, IntPredicate}; +use crate::common::IntPredicate; use crate::traits::*; use crate::MemFlags; +use rustc_hir as hir; use rustc_middle::mir; use rustc_middle::mir::Operand; use rustc_middle::ty::cast::{CastTy, IntTy}; @@ -404,7 +405,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let cast = bx.cx().layout_of(self.monomorphize(mir_cast_ty)); let val = match *kind { - mir::CastKind::PointerExposeAddress => { + mir::CastKind::PointerExposeProvenance => { assert!(bx.cx().is_backend_immediate(cast)); let llptr = operand.immediate(); let llcast_ty = bx.cx().immediate_backend_type(cast); @@ -508,7 +509,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // Since int2ptr can have arbitrary integer types as input (so we have to do // sign extension and all that), it is currently best handled in the same code // path as the other integer-to-X casts. - | mir::CastKind::PointerFromExposedAddress => { + | mir::CastKind::PointerWithExposedProvenance => { assert!(bx.cx().is_backend_immediate(cast)); let ll_t_out = bx.cx().immediate_backend_type(cast); if operand.layout.abi.is_uninhabited() { @@ -860,14 +861,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.inbounds_gep(llty, lhs, &[rhs]) } } - mir::BinOp::Shl => common::build_masked_lshift(bx, lhs, rhs), - mir::BinOp::ShlUnchecked => { - let rhs = base::cast_shift_expr_rhs(bx, lhs, rhs); + mir::BinOp::Shl | mir::BinOp::ShlUnchecked => { + let rhs = base::build_shift_expr_rhs(bx, lhs, rhs, op == mir::BinOp::ShlUnchecked); bx.shl(lhs, rhs) } - mir::BinOp::Shr => common::build_masked_rshift(bx, input_ty, lhs, rhs), - mir::BinOp::ShrUnchecked => { - let rhs = base::cast_shift_expr_rhs(bx, lhs, rhs); + mir::BinOp::Shr | mir::BinOp::ShrUnchecked => { + let rhs = base::build_shift_expr_rhs(bx, lhs, rhs, op == mir::BinOp::ShrUnchecked); if is_signed { bx.ashr(lhs, rhs) } else { bx.lshr(lhs, rhs) } } mir::BinOp::Ne @@ -882,6 +881,35 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.icmp(base::bin_op_to_icmp_predicate(op.to_hir_binop(), is_signed), lhs, rhs) } } + mir::BinOp::Cmp => { + use std::cmp::Ordering; + debug_assert!(!is_float); + let pred = |op| base::bin_op_to_icmp_predicate(op, is_signed); + if bx.cx().tcx().sess.opts.optimize == OptLevel::No { + // FIXME: This actually generates tighter assembly, and is a classic trick + // + // However, as of 2023-11 it optimizes worse in things like derived + // `PartialOrd`, so only use it in debug for now. Once LLVM can handle it + // better (see ), it'll + // be worth trying it in optimized builds as well. + let is_gt = bx.icmp(pred(hir::BinOpKind::Gt), lhs, rhs); + let gtext = bx.zext(is_gt, bx.type_i8()); + let is_lt = bx.icmp(pred(hir::BinOpKind::Lt), lhs, rhs); + let ltext = bx.zext(is_lt, bx.type_i8()); + bx.unchecked_ssub(gtext, ltext) + } else { + // These operations are those expected by `tests/codegen/integer-cmp.rs`, + // from . + let is_lt = bx.icmp(pred(hir::BinOpKind::Lt), lhs, rhs); + let is_ne = bx.icmp(pred(hir::BinOpKind::Ne), lhs, rhs); + let ge = bx.select( + is_ne, + bx.cx().const_i8(Ordering::Greater as i8), + bx.cx().const_i8(Ordering::Equal as i8), + ); + bx.select(is_lt, bx.cx().const_i8(Ordering::Less as i8), ge) + } + } } } diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs index 4dff9c7684f18..8cb17a5b37a89 100644 --- a/compiler/rustc_codegen_ssa/src/traits/consts.rs +++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs @@ -19,6 +19,7 @@ pub trait ConstMethods<'tcx>: BackendTypes { fn const_bool(&self, val: bool) -> Self::Value; fn const_i16(&self, i: i16) -> Self::Value; fn const_i32(&self, i: i32) -> Self::Value; + fn const_i8(&self, i: i8) -> Self::Value; fn const_u32(&self, i: u32) -> Self::Value; fn const_u64(&self, i: u64) -> Self::Value; fn const_u128(&self, i: u128) -> Self::Value; diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index d6aae60c3382a..f6937dc145d5b 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -222,6 +222,7 @@ const_eval_mut_deref = const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind} +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_fmt_macro_call = cannot call non-const formatting macro in {const_eval_const_context}s diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 5c46ec799f1ec..a60cedd6500d5 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -25,6 +25,13 @@ pub(crate) struct DanglingPtrInFinal { pub kind: InternKind, } +#[derive(Diagnostic)] +#[diag(const_eval_nested_static_in_thread_local)] +pub(crate) struct NestedStaticInThreadLocal { + #[primary_span] + pub span: Span, +} + #[derive(LintDiagnostic)] #[diag(const_eval_mutable_ptr_in_final)] pub(crate) struct MutablePtrInFinal { diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index bbf11f169f989..9447d18fe8c93 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -34,15 +34,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.unsize_into(src, cast_layout, dest)?; } - CastKind::PointerExposeAddress => { + CastKind::PointerExposeProvenance => { let src = self.read_immediate(src)?; - let res = self.pointer_expose_address_cast(&src, cast_layout)?; + let res = self.pointer_expose_provenance_cast(&src, cast_layout)?; self.write_immediate(*res, dest)?; } - CastKind::PointerFromExposedAddress => { + CastKind::PointerWithExposedProvenance => { let src = self.read_immediate(src)?; - let res = self.pointer_from_exposed_address_cast(&src, cast_layout)?; + let res = self.pointer_with_exposed_provenance_cast(&src, cast_layout)?; self.write_immediate(*res, dest)?; } @@ -225,7 +225,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } - pub fn pointer_expose_address_cast( + pub fn pointer_expose_provenance_cast( &mut self, src: &ImmTy<'tcx, M::Provenance>, cast_to: TyAndLayout<'tcx>, @@ -242,7 +242,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(ImmTy::from_scalar(self.cast_from_int_like(scalar, src.layout, cast_to.ty)?, cast_to)) } - pub fn pointer_from_exposed_address_cast( + pub fn pointer_with_exposed_provenance_cast( &self, src: &ImmTy<'tcx, M::Provenance>, cast_to: TyAndLayout<'tcx>, diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index 58eaef65e5575..d0f0190fea7e4 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -18,6 +18,7 @@ use rustc_ast::Mutability; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::mir::interpret::{ConstAllocation, CtfeProvenance, InterpResult}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::TyAndLayout; @@ -27,7 +28,7 @@ use rustc_span::sym; use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy}; use crate::const_eval; -use crate::errors::{DanglingPtrInFinal, MutablePtrInFinal}; +use crate::errors::{DanglingPtrInFinal, MutablePtrInFinal, NestedStaticInThreadLocal}; pub trait CompileTimeMachine<'mir, 'tcx: 'mir, T> = Machine< 'mir, @@ -106,13 +107,21 @@ fn intern_as_new_static<'tcx>( DefKind::Static { mutability: alloc.0.mutability, nested: true }, ); tcx.set_nested_alloc_id_static(alloc_id, feed.def_id()); - feed.codegen_fn_attrs(tcx.codegen_fn_attrs(static_id).clone()); + + if tcx.is_thread_local_static(static_id.into()) { + tcx.dcx().emit_err(NestedStaticInThreadLocal { span: tcx.def_span(static_id) }); + } + + // These do not inherit the codegen attrs of the parent static allocation, since + // it doesn't make sense for them to inherit their `#[no_mangle]` and `#[link_name = ..]` + // and the like. + feed.codegen_fn_attrs(CodegenFnAttrs::new()); + feed.eval_static_initializer(Ok(alloc)); feed.generics_of(tcx.generics_of(static_id).clone()); feed.def_ident_span(tcx.def_ident_span(static_id)); feed.explicit_predicates_of(tcx.explicit_predicates_of(static_id)); - - feed.feed_hir() + feed.feed_hir(); } /// How a constant value should be interned. diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index dbc6a317640c1..842fb6d204c29 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -235,6 +235,13 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { Self::from_scalar(Scalar::from_bool(b), layout) } + #[inline] + pub fn from_ordering(c: std::cmp::Ordering, tcx: TyCtxt<'tcx>) -> Self { + let ty = tcx.ty_ordering_enum(None); + let layout = tcx.layout_of(ty::ParamEnv::reveal_all().and(ty)).unwrap(); + Self::from_scalar(Scalar::from_i8(c as i8), layout) + } + #[inline] pub fn to_const_int(self) -> ConstInt { assert!(self.layout.ty.is_integral()); @@ -785,7 +792,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } // Some nodes are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index 475c533077af5..5665bb4999f59 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -61,6 +61,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { + fn three_way_compare(&self, lhs: T, rhs: T) -> (ImmTy<'tcx, M::Provenance>, bool) { + let res = Ord::cmp(&lhs, &rhs); + return (ImmTy::from_ordering(res, *self.tcx), false); + } + fn binary_char_op( &self, bin_op: mir::BinOp, @@ -69,6 +74,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) -> (ImmTy<'tcx, M::Provenance>, bool) { use rustc_middle::mir::BinOp::*; + if bin_op == Cmp { + return self.three_way_compare(l, r); + } + let res = match bin_op { Eq => l == r, Ne => l != r, @@ -231,6 +240,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let r = self.sign_extend(r, right_layout) as i128; return Ok((ImmTy::from_bool(op(&l, &r), *self.tcx), false)); } + if bin_op == Cmp { + let l = self.sign_extend(l, left_layout) as i128; + let r = self.sign_extend(r, right_layout) as i128; + return Ok(self.three_way_compare(l, r)); + } let op: Option (i128, bool)> = match bin_op { Div if r == 0 => throw_ub!(DivisionByZero), Rem if r == 0 => throw_ub!(RemainderByZero), @@ -270,6 +284,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } + if bin_op == Cmp { + return Ok(self.three_way_compare(l, r)); + } + let val = match bin_op { Eq => ImmTy::from_bool(l == r, *self.tcx), Ne => ImmTy::from_bool(l != r, *self.tcx), diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 1a2f1194f89a2..e32aea39fc597 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -1058,7 +1058,7 @@ where } // Some nodes are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index da8e28d02982e..543996c86baca 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -544,10 +544,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // Unsizing is implemented for CTFE. } - Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => { + Rvalue::Cast(CastKind::PointerExposeProvenance, _, _) => { self.check_op(ops::RawPtrToIntCast); } - Rvalue::Cast(CastKind::PointerFromExposedAddress, _, _) => { + Rvalue::Cast(CastKind::PointerWithExposedProvenance, _, _) => { // Since no pointer can ever get exposed (rejected above), this is easy to support. } diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 378b168a50c33..a499e4b980fc3 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -986,6 +986,15 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ) } } + Cmp => { + for x in [a, b] { + check_kinds!( + x, + "Cannot three-way compare non-integer type {:?}", + ty::Char | ty::Uint(..) | ty::Int(..) + ) + } + } AddUnchecked | SubUnchecked | MulUnchecked | Shl | ShlUnchecked | Shr | ShrUnchecked => { for x in [a, b] { @@ -1067,8 +1076,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { // FIXME(dyn-star): make sure nothing needs to be done here. } // FIXME: Add Checks for these - CastKind::PointerFromExposedAddress - | CastKind::PointerExposeAddress + CastKind::PointerWithExposedProvenance + | CastKind::PointerExposeProvenance | CastKind::PointerCoercion(_) => {} CastKind::IntToInt | CastKind::IntToFloat => { let input_valid = op_ty.is_integral() || op_ty.is_char() || op_ty.is_bool(); diff --git a/compiler/rustc_const_eval/src/util/mod.rs b/compiler/rustc_const_eval/src/util/mod.rs index a8060463b69ed..0c3b59a0e78ec 100644 --- a/compiler/rustc_const_eval/src/util/mod.rs +++ b/compiler/rustc_const_eval/src/util/mod.rs @@ -19,7 +19,7 @@ pub fn binop_left_homogeneous(op: mir::BinOp) -> bool { match op { Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor | BitAnd | BitOr | Offset | Shl | ShlUnchecked | Shr | ShrUnchecked => true, - Eq | Ne | Lt | Le | Gt | Ge => false, + Eq | Ne | Lt | Le | Gt | Ge | Cmp => false, } } @@ -30,7 +30,7 @@ pub fn binop_right_homogeneous(op: mir::BinOp) -> bool { use rustc_middle::mir::BinOp::*; match op { Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor - | BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge => true, + | BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge | Cmp => true, Offset | Shl | ShlUnchecked | Shr | ShrUnchecked => false, } } diff --git a/compiler/rustc_data_structures/src/graph/dominators/mod.rs b/compiler/rustc_data_structures/src/graph/dominators/mod.rs index a45f1dd72a126..30e240cf85b84 100644 --- a/compiler/rustc_data_structures/src/graph/dominators/mod.rs +++ b/compiler/rustc_data_structures/src/graph/dominators/mod.rs @@ -72,7 +72,7 @@ fn dominators_impl(graph: &G) -> Inner { IndexVec::with_capacity(graph.num_nodes()); let mut stack = vec![PreOrderFrame { - pre_order_idx: PreorderIndex::new(0), + pre_order_idx: PreorderIndex::ZERO, iter: graph.successors(graph.start_node()), }]; let mut pre_order_to_real: IndexVec = @@ -80,8 +80,8 @@ fn dominators_impl(graph: &G) -> Inner { let mut real_to_pre_order: IndexVec> = IndexVec::from_elem_n(None, graph.num_nodes()); pre_order_to_real.push(graph.start_node()); - parent.push(PreorderIndex::new(0)); // the parent of the root node is the root for now. - real_to_pre_order[graph.start_node()] = Some(PreorderIndex::new(0)); + parent.push(PreorderIndex::ZERO); // the parent of the root node is the root for now. + real_to_pre_order[graph.start_node()] = Some(PreorderIndex::ZERO); let mut post_order_idx = 0; // Traverse the graph, collecting a number of things: @@ -111,7 +111,7 @@ fn dominators_impl(graph: &G) -> Inner { let reachable_vertices = pre_order_to_real.len(); - let mut idom = IndexVec::from_elem_n(PreorderIndex::new(0), reachable_vertices); + let mut idom = IndexVec::from_elem_n(PreorderIndex::ZERO, reachable_vertices); let mut semi = IndexVec::from_fn_n(std::convert::identity, reachable_vertices); let mut label = semi.clone(); let mut bucket = IndexVec::from_elem_n(vec![], reachable_vertices); diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index 15691804a94b2..8418b4bbd4702 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -684,26 +684,11 @@ where impl_stable_traits_for_trivial_type!(::std::path::Path); impl_stable_traits_for_trivial_type!(::std::path::PathBuf); -impl HashStable for ::std::collections::HashMap -where - K: ToStableHashKey + Eq, - V: HashStable, - R: BuildHasher, -{ - #[inline] - fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { - stable_hash_reduce(hcx, hasher, self.iter(), self.len(), |hasher, hcx, (key, value)| { - let key = key.to_stable_hash_key(hcx); - key.hash_stable(hcx, hasher); - value.hash_stable(hcx, hasher); - }); - } -} - -// It is not safe to implement HashStable for HashSet or any other collection type +// It is not safe to implement HashStable for HashSet, HashMap or any other collection type // with unstable but observable iteration order. // See https://github.com/rust-lang/compiler-team/issues/533 for further information. impl !HashStable for std::collections::HashSet {} +impl !HashStable for std::collections::HashMap {} impl HashStable for ::std::collections::BTreeMap where @@ -730,35 +715,6 @@ where } } -fn stable_hash_reduce( - hcx: &mut HCX, - hasher: &mut StableHasher, - mut collection: C, - length: usize, - hash_function: F, -) where - C: Iterator, - F: Fn(&mut StableHasher, &mut HCX, I), -{ - length.hash_stable(hcx, hasher); - - match length { - 1 => { - hash_function(hasher, hcx, collection.next().unwrap()); - } - _ => { - let hash = collection - .map(|value| { - let mut hasher = StableHasher::new(); - hash_function(&mut hasher, hcx, value); - hasher.finish::() - }) - .reduce(|accum, value| accum.wrapping_add(value)); - hash.hash_stable(hcx, hasher); - } - } -} - /// Controls what data we do or do not hash. /// Whenever a `HashStable` implementation caches its /// result, it needs to include `HashingControls` as part diff --git a/compiler/rustc_data_structures/src/tagged_ptr/copy.rs b/compiler/rustc_data_structures/src/tagged_ptr/copy.rs index ff4208def319d..8b9e834b60b0d 100644 --- a/compiler/rustc_data_structures/src/tagged_ptr/copy.rs +++ b/compiler/rustc_data_structures/src/tagged_ptr/copy.rs @@ -243,6 +243,7 @@ where T: Tag, { #[inline] + #[allow(ambiguous_wide_pointer_comparisons)] fn eq(&self, other: &Self) -> bool { self.packed == other.packed } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 7b40954e735db..b4107bd4a2bad 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -102,9 +102,9 @@ pub type PResult<'a, T> = Result>; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } // `PResult` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(PResult<'_, ()>, 16); -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(PResult<'_, bool>, 16); #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Encodable, Decodable)] @@ -1951,6 +1951,39 @@ pub fn report_ambiguity_error<'a, G: EmissionGuarantee>( } } +/// Grammatical tool for displaying messages to end users in a nice form. +/// +/// Returns "an" if the given string starts with a vowel, and "a" otherwise. +pub fn a_or_an(s: &str) -> &'static str { + let mut chars = s.chars(); + let Some(mut first_alpha_char) = chars.next() else { + return "a"; + }; + if first_alpha_char == '`' { + let Some(next) = chars.next() else { + return "a"; + }; + first_alpha_char = next; + } + if ["a", "e", "i", "o", "u", "&"].contains(&&first_alpha_char.to_lowercase().to_string()[..]) { + "an" + } else { + "a" + } +} + +/// Grammatical tool for displaying messages to end users in a nice form. +/// +/// Take a list ["a", "b", "c"] and output a display friendly version "a, b and c" +pub fn display_list_with_comma_and(v: &[T]) -> String { + match v.len() { + 0 => "".to_string(), + 1 => v[0].to_string(), + 2 => format!("{} and {}", v[0], v[1]), + _ => format!("{}, {}", v[0], display_list_with_comma_and(&v[1..])), + } +} + #[derive(Clone, Copy, PartialEq, Hash, Debug)] pub enum TerminalUrl { No, diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 30559871b4e2c..cdcf67b26f80a 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -48,6 +48,23 @@ impl<'a> ExtCtxt<'a> { ast::Path { span, segments, tokens: None } } + pub fn macro_call( + &self, + span: Span, + path: ast::Path, + delim: ast::token::Delimiter, + tokens: ast::tokenstream::TokenStream, + ) -> P { + P(ast::MacCall { + path, + args: P(ast::DelimArgs { + dspan: ast::tokenstream::DelimSpan { open: span, close: span }, + delim, + tokens, + }), + }) + } + pub fn ty_mt(&self, ty: P, mutbl: ast::Mutability) -> ast::MutTy { ast::MutTy { ty, mutbl } } @@ -265,6 +282,10 @@ impl<'a> ExtCtxt<'a> { self.expr(span, ast::ExprKind::Field(expr, field)) } + pub fn expr_macro_call(&self, span: Span, call: P) -> P { + self.expr(span, ast::ExprKind::MacCall(call)) + } + pub fn expr_binary( &self, sp: Span, @@ -410,18 +431,21 @@ impl<'a> ExtCtxt<'a> { self.expr(sp, ast::ExprKind::Tup(exprs)) } - pub fn expr_fail(&self, span: Span, msg: Symbol) -> P { - self.expr_call_global( + pub fn expr_unreachable(&self, span: Span) -> P { + self.expr_macro_call( span, - [sym::std, sym::rt, sym::begin_panic].iter().map(|s| Ident::new(*s, span)).collect(), - thin_vec![self.expr_str(span, msg)], + self.macro_call( + span, + self.path_global( + span, + [sym::std, sym::unreachable].map(|s| Ident::new(s, span)).to_vec(), + ), + ast::token::Delimiter::Parenthesis, + ast::tokenstream::TokenStream::default(), + ), ) } - pub fn expr_unreachable(&self, span: Span) -> P { - self.expr_fail(span, Symbol::intern("internal error: entered unreachable code")) - } - pub fn expr_ok(&self, sp: Span, expr: P) -> P { let ok = self.std_path(&[sym::result, sym::Result, sym::Ok]); self.expr_call_global(sp, ok, thin_vec![expr]) diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index cac1e8f80e323..6029caa965c0b 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -871,7 +871,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let mut parser = self.cx.new_parser_from_tts(toks); match parse_ast_fragment(&mut parser, kind) { Ok(fragment) => { - ensure_complete_parse(&mut parser, path, kind.name(), span); + ensure_complete_parse(&parser, path, kind.name(), span); fragment } Err(mut err) => { @@ -958,7 +958,7 @@ pub fn parse_ast_fragment<'a>( } pub fn ensure_complete_parse<'a>( - parser: &mut Parser<'a>, + parser: &Parser<'a>, macro_path: &ast::Path, kind_name: &str, span: Span, diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index a31be05ccc4d2..9fff00ffeae17 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -266,7 +266,7 @@ struct MatcherPos { } // This type is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(MatcherPos, 16); impl MatcherPos { diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 22cf50fce7f49..6a8a1722bcb23 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -821,6 +821,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::No, "#[rustc_allow_incoherent_impl] has to be added to all impl items of an incoherent inherent impl." ), + rustc_attr!( + rustc_preserve_ub_checks, AttributeType::CrateLevel, template!(Word), ErrorFollowing, EncodeCrossCrate::No, + "`#![rustc_preserve_ub_checks]` prevents the designated crate from evaluating whether UB checks are enabled when optimizing MIR", + ), rustc_attr!( rustc_deny_explicit_impl, AttributeType::Normal, diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 8d72f4924d63c..36db377f7e0a8 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -529,6 +529,8 @@ declare_features! ( (unstable, more_qualified_paths, "1.54.0", Some(86935)), /// Allows the `#[must_not_suspend]` attribute. (unstable, must_not_suspend, "1.57.0", Some(83310)), + /// Allows `mut ref` and `mut ref mut` identifier patterns. + (incomplete, mut_ref, "CURRENT_RUSTC_VERSION", Some(123076)), /// Allows using `#[naked]` on functions. (unstable, naked_functions, "1.9.0", Some(90957)), /// Allows specifying the as-needed link modifier @@ -565,6 +567,8 @@ declare_features! ( (unstable, proc_macro_hygiene, "1.30.0", Some(54727)), /// Allows `&raw const $place_expr` and `&raw mut $place_expr` expressions. (unstable, raw_ref_op, "1.41.0", Some(64490)), + /// Allows `&` and `&mut` patterns to consume match-ergonomics-inserted references. + (incomplete, ref_pat_everywhere, "CURRENT_RUSTC_VERSION", Some(123076)), /// Allows using the `#[register_tool]` attribute. (unstable, register_tool, "1.41.0", Some(66079)), /// Allows the `#[repr(i128)]` attribute for enums. diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs index f538d6bcb9816..cd5da279a2621 100644 --- a/compiler/rustc_hir/src/definitions.rs +++ b/compiler/rustc_hir/src/definitions.rs @@ -380,14 +380,19 @@ impl Definitions { pub fn local_def_path_hash_to_def_id( &self, hash: DefPathHash, - err: &mut dyn FnMut() -> !, + err_msg: &dyn std::fmt::Debug, ) -> LocalDefId { debug_assert!(hash.stable_crate_id() == self.table.stable_crate_id); + #[cold] + #[inline(never)] + fn err(err_msg: &dyn std::fmt::Debug) -> ! { + panic!("{err_msg:?}") + } self.table .def_path_hash_to_index .get(&hash.local_hash()) .map(|local_def_index| LocalDefId { local_def_index }) - .unwrap_or_else(|| err()) + .unwrap_or_else(|| err(err_msg)) } pub fn def_path_hash_to_def_index_map(&self) -> &DefPathHashMap { diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index a70d2ebbd6209..f21cd653f962b 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -846,9 +846,8 @@ pub struct OwnerNodes<'tcx> { impl<'tcx> OwnerNodes<'tcx> { pub fn node(&self) -> OwnerNode<'tcx> { - use rustc_index::Idx; // Indexing must ensure it is an OwnerNode. - self.nodes[ItemLocalId::new(0)].node.as_owner().unwrap() + self.nodes[ItemLocalId::ZERO].node.as_owner().unwrap() } } @@ -856,7 +855,7 @@ impl fmt::Debug for OwnerNodes<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("OwnerNodes") // Do not print all the pointers to all the nodes, as it would be unreadable. - .field("node", &self.nodes[ItemLocalId::from_u32(0)]) + .field("node", &self.nodes[ItemLocalId::ZERO]) .field( "parents", &self @@ -2733,9 +2732,9 @@ pub enum ImplicitSelfKind { /// Represents a `fn x(mut self);`. Mut, /// Represents a `fn x(&self);`. - ImmRef, + RefImm, /// Represents a `fn x(&mut self);`. - MutRef, + RefMut, /// Represents when a function does not have a self argument or /// when a function has a `self: X` argument. None, @@ -3762,7 +3761,7 @@ impl<'hir> Node<'hir> { } // Some nodes are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] mod size_asserts { use super::*; // tidy-alphabetical-start diff --git a/compiler/rustc_hir/src/hir_id.rs b/compiler/rustc_hir/src/hir_id.rs index d339075c171d1..0341a482fa8c1 100644 --- a/compiler/rustc_hir/src/hir_id.rs +++ b/compiler/rustc_hir/src/hir_id.rs @@ -17,7 +17,7 @@ impl Debug for OwnerId { impl From for HirId { fn from(owner: OwnerId) -> HirId { - HirId { owner, local_id: ItemLocalId::from_u32(0) } + HirId { owner, local_id: ItemLocalId::ZERO } } } @@ -110,7 +110,7 @@ impl HirId { #[inline] pub fn make_owner(owner: LocalDefId) -> Self { - Self { owner: OwnerId { def_id: owner }, local_id: ItemLocalId::from_u32(0) } + Self { owner: OwnerId { def_id: owner }, local_id: ItemLocalId::ZERO } } pub fn index(self) -> (usize, usize) { @@ -172,6 +172,6 @@ unsafe impl StableOrd for ItemLocalId { /// The `HirId` corresponding to `CRATE_NODE_ID` and `CRATE_DEF_ID`. pub const CRATE_HIR_ID: HirId = - HirId { owner: OwnerId { def_id: CRATE_DEF_ID }, local_id: ItemLocalId::from_u32(0) }; + HirId { owner: OwnerId { def_id: CRATE_DEF_ID }, local_id: ItemLocalId::ZERO }; pub const CRATE_OWNER_ID: OwnerId = OwnerId { def_id: CRATE_DEF_ID }; diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 5d97019416f8f..2a796ca5465c9 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -226,6 +226,7 @@ language_item_table! { Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None; Pin, sym::pin, pin_type, Target::Struct, GenericRequirement::None; + OrderingEnum, sym::Ordering, ordering_enum, Target::Enum, GenericRequirement::Exact(0); PartialEq, sym::eq, eq_trait, Target::Trait, GenericRequirement::Exact(1); PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1); CVoid, sym::c_void, c_void, Target::Enum, GenericRequirement::None; @@ -248,6 +249,25 @@ language_item_table! { PanicImpl, sym::panic_impl, panic_impl, Target::Fn, GenericRequirement::None; PanicCannotUnwind, sym::panic_cannot_unwind, panic_cannot_unwind, Target::Fn, GenericRequirement::Exact(0); PanicInCleanup, sym::panic_in_cleanup, panic_in_cleanup, Target::Fn, GenericRequirement::Exact(0); + /// Constant panic messages, used for codegen of MIR asserts. + PanicAddOverflow, sym::panic_const_add_overflow, panic_const_add_overflow, Target::Fn, GenericRequirement::None; + PanicSubOverflow, sym::panic_const_sub_overflow, panic_const_sub_overflow, Target::Fn, GenericRequirement::None; + PanicMulOverflow, sym::panic_const_mul_overflow, panic_const_mul_overflow, Target::Fn, GenericRequirement::None; + PanicDivOverflow, sym::panic_const_div_overflow, panic_const_div_overflow, Target::Fn, GenericRequirement::None; + PanicRemOverflow, sym::panic_const_rem_overflow, panic_const_rem_overflow, Target::Fn, GenericRequirement::None; + PanicNegOverflow, sym::panic_const_neg_overflow, panic_const_neg_overflow, Target::Fn, GenericRequirement::None; + PanicShrOverflow, sym::panic_const_shr_overflow, panic_const_shr_overflow, Target::Fn, GenericRequirement::None; + PanicShlOverflow, sym::panic_const_shl_overflow, panic_const_shl_overflow, Target::Fn, GenericRequirement::None; + PanicDivZero, sym::panic_const_div_by_zero, panic_const_div_by_zero, Target::Fn, GenericRequirement::None; + PanicRemZero, sym::panic_const_rem_by_zero, panic_const_rem_by_zero, Target::Fn, GenericRequirement::None; + PanicCoroutineResumed, sym::panic_const_coroutine_resumed, panic_const_coroutine_resumed, Target::Fn, GenericRequirement::None; + PanicAsyncFnResumed, sym::panic_const_async_fn_resumed, panic_const_async_fn_resumed, Target::Fn, GenericRequirement::None; + PanicAsyncGenFnResumed, sym::panic_const_async_gen_fn_resumed, panic_const_async_gen_fn_resumed, Target::Fn, GenericRequirement::None; + PanicGenFnNone, sym::panic_const_gen_fn_none, panic_const_gen_fn_none, Target::Fn, GenericRequirement::None; + PanicCoroutineResumedPanic, sym::panic_const_coroutine_resumed_panic, panic_const_coroutine_resumed_panic, Target::Fn, GenericRequirement::None; + PanicAsyncFnResumedPanic, sym::panic_const_async_fn_resumed_panic, panic_const_async_fn_resumed_panic, Target::Fn, GenericRequirement::None; + PanicAsyncGenFnResumedPanic, sym::panic_const_async_gen_fn_resumed_panic, panic_const_async_gen_fn_resumed_panic, Target::Fn, GenericRequirement::None; + PanicGenFnNonePanic, sym::panic_const_gen_fn_none_panic, panic_const_gen_fn_none_panic, Target::Fn, GenericRequirement::None; /// libstd panic entry point. Necessary for const eval to be able to catch it BeginPanic, sym::begin_panic, begin_panic_fn, Target::Fn, GenericRequirement::None; diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 9bf4d63267a39..d8a90d62dac69 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -295,6 +295,8 @@ hir_analysis_not_supported_delegation = {$descr} is not supported yet .label = callee defined here +hir_analysis_only_current_traits_adt = `{$name}` is not defined in the current crate + hir_analysis_only_current_traits_arbitrary = only traits defined in the current crate can be implemented for arbitrary types hir_analysis_only_current_traits_foreign = this is not defined in the current crate because this is a foreign trait diff --git a/compiler/rustc_hir_analysis/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs index d5465bb5dd54a..d3f51195dfb29 100644 --- a/compiler/rustc_hir_analysis/src/bounds.rs +++ b/compiler/rustc_hir_analysis/src/bounds.rs @@ -54,14 +54,20 @@ impl<'tcx> Bounds<'tcx> { span: Span, polarity: ty::PredicatePolarity, ) { - self.clauses.push(( + let clause = ( trait_ref .map_bound(|trait_ref| { ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, polarity }) }) .to_predicate(tcx), span, - )); + ); + // FIXME(-Znext-solver): We can likely remove this hack once the new trait solver lands. + if tcx.lang_items().sized_trait() == Some(trait_ref.def_id()) { + self.clauses.insert(0, clause); + } else { + self.clauses.push(clause); + } } pub fn push_projection_bound( diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 1286a724e9574..739a708699239 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -899,7 +899,7 @@ pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) { struct_span_code_err!(tcx.dcx(), sp, E0075, "SIMD vector cannot be empty").emit(); return; } - let e = fields[FieldIdx::from_u32(0)].ty(tcx, args); + let e = fields[FieldIdx::ZERO].ty(tcx, args); if !fields.iter().all(|f| f.ty(tcx, args) == e) { struct_span_code_err!(tcx.dcx(), sp, E0076, "SIMD vector should be homogeneous") .with_span_label(sp, "SIMD elements must have the same type") @@ -964,7 +964,7 @@ pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) { for r in attr::parse_repr_attr(tcx.sess, attr) { if let attr::ReprPacked(pack) = r && let Some(repr_pack) = repr.pack - && pack as u64 != repr_pack.bytes() + && pack != repr_pack { struct_span_code_err!( tcx.dcx(), diff --git a/compiler/rustc_hir_analysis/src/check/errs.rs b/compiler/rustc_hir_analysis/src/check/errs.rs index f0c15a070b48a..548f9b0810fa3 100644 --- a/compiler/rustc_hir_analysis/src/check/errs.rs +++ b/compiler/rustc_hir_analysis/src/check/errs.rs @@ -14,14 +14,7 @@ pub fn maybe_expr_static_mut(tcx: TyCtxt<'_>, expr: hir::Expr<'_>) { && matches!(borrow_kind, hir::BorrowKind::Ref) && let Some(var) = is_path_static_mut(*expr) { - handle_static_mut_ref( - tcx, - span, - var, - span.edition().at_least_rust_2024(), - matches!(m, Mutability::Mut), - hir_id, - ); + handle_static_mut_ref(tcx, span, var, span.edition().at_least_rust_2024(), m, hir_id); } } @@ -29,7 +22,7 @@ pub fn maybe_expr_static_mut(tcx: TyCtxt<'_>, expr: hir::Expr<'_>) { pub fn maybe_stmt_static_mut(tcx: TyCtxt<'_>, stmt: hir::Stmt<'_>) { if let hir::StmtKind::Let(loc) = stmt.kind && let hir::PatKind::Binding(ba, _, _, _) = loc.pat.kind - && matches!(ba.0, rustc_ast::ByRef::Yes) + && let hir::ByRef::Yes(rmutbl) = ba.0 && let Some(init) = loc.init && let Some(var) = is_path_static_mut(*init) { @@ -38,7 +31,7 @@ pub fn maybe_stmt_static_mut(tcx: TyCtxt<'_>, stmt: hir::Stmt<'_>) { init.span, var, loc.span.edition().at_least_rust_2024(), - matches!(ba.1, Mutability::Mut), + rmutbl, stmt.hir_id, ); } @@ -60,28 +53,27 @@ fn handle_static_mut_ref( span: Span, var: String, e2024: bool, - mutable: bool, + mutable: Mutability, hir_id: hir::HirId, ) { if e2024 { - let (sugg, shared) = if mutable { + let (sugg, shared) = if mutable == Mutability::Mut { (errors::StaticMutRefSugg::Mut { span, var }, "mutable") } else { (errors::StaticMutRefSugg::Shared { span, var }, "shared") }; tcx.sess.psess.dcx.emit_err(errors::StaticMutRef { span, sugg, shared }); - return; - } - - let (sugg, shared) = if mutable { - (errors::RefOfMutStaticSugg::Mut { span, var }, "mutable") } else { - (errors::RefOfMutStaticSugg::Shared { span, var }, "shared") - }; - tcx.emit_node_span_lint( - STATIC_MUT_REFS, - hir_id, - span, - errors::RefOfMutStatic { span, sugg, shared }, - ); + let (sugg, shared) = if mutable == Mutability::Mut { + (errors::RefOfMutStaticSugg::Mut { span, var }, "mutable") + } else { + (errors::RefOfMutStaticSugg::Shared { span, var }, "shared") + }; + tcx.emit_node_span_lint( + STATIC_MUT_REFS, + hir_id, + span, + errors::RefOfMutStatic { span, sugg, shared }, + ); + } } diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index f482ae4f5fa07..bd64621f07738 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -107,6 +107,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) - | sym::cttz | sym::bswap | sym::bitreverse + | sym::three_way_compare | sym::discriminant_value | sym::type_id | sym::likely @@ -182,7 +183,7 @@ pub fn check_intrinsic_type( let region = ty::Region::new_bound( tcx, ty::INNERMOST, - ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon }, + ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BrAnon }, ); let env_region = ty::Region::new_bound( tcx, @@ -418,6 +419,10 @@ pub fn check_intrinsic_type( | sym::bswap | sym::bitreverse => (1, 0, vec![param(0)], param(0)), + sym::three_way_compare => { + (1, 0, vec![param(0), param(0)], tcx.ty_ordering_enum(Some(span))) + } + sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => { (1, 0, vec![param(0), param(0)], Ty::new_tup(tcx, &[param(0), tcx.types.bool])) } @@ -454,9 +459,8 @@ pub fn check_intrinsic_type( sym::unchecked_div | sym::unchecked_rem | sym::exact_div => { (1, 0, vec![param(0), param(0)], param(0)) } - sym::unchecked_shl | sym::unchecked_shr | sym::rotate_left | sym::rotate_right => { - (1, 0, vec![param(0), param(0)], param(0)) - } + sym::unchecked_shl | sym::unchecked_shr => (2, 0, vec![param(0), param(1)], param(0)), + sym::rotate_left | sym::rotate_right => (1, 0, vec![param(0), param(0)], param(0)), sym::unchecked_add | sym::unchecked_sub | sym::unchecked_mul => { (1, 0, vec![param(0), param(0)], param(0)) } @@ -491,7 +495,7 @@ pub fn check_intrinsic_type( ); let discriminant_def_id = assoc_items[0]; - let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon }; + let br = ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BrAnon }; ( 1, 0, @@ -551,7 +555,7 @@ pub fn check_intrinsic_type( } sym::raw_eq => { - let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon }; + let br = ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BrAnon }; let param_ty_lhs = Ty::new_imm_ref(tcx, ty::Region::new_bound(tcx, ty::INNERMOST, br), param(0)); let br = ty::BoundRegion { var: ty::BoundVar::from_u32(1), kind: ty::BrAnon }; @@ -623,8 +627,8 @@ pub fn check_intrinsic_type( sym::simd_cast | sym::simd_as | sym::simd_cast_ptr - | sym::simd_expose_addr - | sym::simd_from_exposed_addr => (2, 0, vec![param(0)], param(1)), + | sym::simd_expose_provenance + | sym::simd_with_exposed_provenance => (2, 0, vec![param(0)], param(1)), sym::simd_bitmask => (2, 0, vec![param(0)], param(1)), sym::simd_select | sym::simd_select_bitmask => { (2, 0, vec![param(0), param(1), param(1)], param(1)) diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs index df4db3ec3fbd3..1958a80d47c18 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs @@ -67,7 +67,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { ty::RawPtr(ty, _) if self.is_thin_ptr_ty(ty) => Some(asm_ty_isize), ty::Adt(adt, args) if adt.repr().simd() => { let fields = &adt.non_enum_variant().fields; - let elem_ty = fields[FieldIdx::from_u32(0)].ty(self.tcx, args); + let elem_ty = fields[FieldIdx::ZERO].ty(self.tcx, args); let (size, ty) = match elem_ty.kind() { ty::Array(ty, len) => { @@ -146,7 +146,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { "expected first field of `MaybeUnit` to be `ManuallyDrop`" ); let fields = &ty.non_enum_variant().fields; - let ty = fields[FieldIdx::from_u32(0)].ty(self.tcx, args); + let ty = fields[FieldIdx::ZERO].ty(self.tcx, args); self.get_asm_ty(ty) } _ => self.get_asm_ty(ty), diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index dcabac6d78012..3bdb9a214ecd7 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -654,7 +654,7 @@ fn resolve_local<'tcx>( // & expression, and its lifetime would be extended to the end of the block (due // to a different rule, not the below code). match pat.kind { - PatKind::Binding(hir::BindingAnnotation(hir::ByRef::Yes, _), ..) => true, + PatKind::Binding(hir::BindingAnnotation(hir::ByRef::Yes(_), _), ..) => true, PatKind::Struct(_, field_pats, _) => field_pats.iter().any(|fp| is_binding_pat(fp.pat)), diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index ca8a635ab5eb9..1770f7b4e917c 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -4,7 +4,7 @@ use crate::errors; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; -use rustc_middle::ty::{self, AliasKind, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, AliasKind, TyCtxt, TypeVisitableExt}; use rustc_span::def_id::LocalDefId; use rustc_span::Span; use rustc_trait_selection::traits::{self, IsFirstInputType}; @@ -283,9 +283,14 @@ fn emit_orphan_check_error<'tcx>( let self_ty = trait_ref.self_ty(); Err(match err { traits::OrphanCheckErr::NonLocalInputType(tys) => { - let (mut opaque, mut foreign, mut name, mut pointer, mut ty_diag) = - (Vec::new(), Vec::new(), Vec::new(), Vec::new(), Vec::new()); - let mut sugg = None; + let mut diag = tcx.dcx().create_err(match self_ty.kind() { + ty::Adt(..) => errors::OnlyCurrentTraits::Outside { span: sp, note: () }, + _ if self_ty.is_primitive() => { + errors::OnlyCurrentTraits::Primitive { span: sp, note: () } + } + _ => errors::OnlyCurrentTraits::Arbitrary { span: sp, note: () }, + }); + for &(mut ty, is_target_ty) in &tys { let span = if matches!(is_target_ty, IsFirstInputType::Yes) { // Point at `D` in `impl for C in D` @@ -296,113 +301,86 @@ fn emit_orphan_check_error<'tcx>( }; ty = tcx.erase_regions(ty); - ty = match ty.kind() { - // Remove the type arguments from the output, as they are not relevant. - // You can think of this as the reverse of `resolve_vars_if_possible`. - // That way if we had `Vec`, we will properly attribute the - // problem to `Vec` and avoid confusing the user if they were to see - // `MyType` in the error. - ty::Adt(def, _) => Ty::new_adt(tcx, *def, ty::List::empty()), - _ => ty, - }; - - fn push_to_foreign_or_name<'tcx>( - is_foreign: bool, - foreign: &mut Vec, - name: &mut Vec>, - span: Span, - sname: &'tcx str, - ) { - if is_foreign { - foreign.push(errors::OnlyCurrentTraitsForeign { span }) - } else { - name.push(errors::OnlyCurrentTraitsName { span, name: sname }); - } - } let is_foreign = !trait_ref.def_id.is_local() && matches!(is_target_ty, IsFirstInputType::No); match *ty.kind() { ty::Slice(_) => { - push_to_foreign_or_name( - is_foreign, - &mut foreign, - &mut name, - span, - "slices", - ); + if is_foreign { + diag.subdiagnostic( + tcx.dcx(), + errors::OnlyCurrentTraitsForeign { span }, + ); + } else { + diag.subdiagnostic( + tcx.dcx(), + errors::OnlyCurrentTraitsName { span, name: "slices" }, + ); + } } ty::Array(..) => { - push_to_foreign_or_name( - is_foreign, - &mut foreign, - &mut name, - span, - "arrays", - ); + if is_foreign { + diag.subdiagnostic( + tcx.dcx(), + errors::OnlyCurrentTraitsForeign { span }, + ); + } else { + diag.subdiagnostic( + tcx.dcx(), + errors::OnlyCurrentTraitsName { span, name: "arrays" }, + ); + } } ty::Tuple(..) => { - push_to_foreign_or_name( - is_foreign, - &mut foreign, - &mut name, - span, - "tuples", - ); + if is_foreign { + diag.subdiagnostic( + tcx.dcx(), + errors::OnlyCurrentTraitsForeign { span }, + ); + } else { + diag.subdiagnostic( + tcx.dcx(), + errors::OnlyCurrentTraitsName { span, name: "tuples" }, + ); + } } ty::Alias(ty::Opaque, ..) => { - opaque.push(errors::OnlyCurrentTraitsOpaque { span }) + diag.subdiagnostic(tcx.dcx(), errors::OnlyCurrentTraitsOpaque { span }); } ty::RawPtr(ptr_ty, mutbl) => { if !self_ty.has_param() { - let mut_key = mutbl.prefix_str(); - sugg = Some(errors::OnlyCurrentTraitsPointerSugg { - wrapper_span: self_ty_span, - struct_span: full_impl_span.shrink_to_lo(), - mut_key, - ptr_ty, - }); + diag.subdiagnostic( + tcx.dcx(), + errors::OnlyCurrentTraitsPointerSugg { + wrapper_span: self_ty_span, + struct_span: full_impl_span.shrink_to_lo(), + mut_key: mutbl.prefix_str(), + ptr_ty, + }, + ); } - pointer.push(errors::OnlyCurrentTraitsPointer { span, pointer: ty }); + diag.subdiagnostic( + tcx.dcx(), + errors::OnlyCurrentTraitsPointer { span, pointer: ty }, + ); + } + ty::Adt(adt_def, _) => { + diag.subdiagnostic( + tcx.dcx(), + errors::OnlyCurrentTraitsAdt { + span, + name: tcx.def_path_str(adt_def.did()), + }, + ); + } + _ => { + diag.subdiagnostic(tcx.dcx(), errors::OnlyCurrentTraitsTy { span, ty }); } - _ => ty_diag.push(errors::OnlyCurrentTraitsTy { span, ty }), } } - let err_struct = match self_ty.kind() { - ty::Adt(..) => errors::OnlyCurrentTraits::Outside { - span: sp, - note: (), - opaque, - foreign, - name, - pointer, - ty: ty_diag, - sugg, - }, - _ if self_ty.is_primitive() => errors::OnlyCurrentTraits::Primitive { - span: sp, - note: (), - opaque, - foreign, - name, - pointer, - ty: ty_diag, - sugg, - }, - _ => errors::OnlyCurrentTraits::Arbitrary { - span: sp, - note: (), - opaque, - foreign, - name, - pointer, - ty: ty_diag, - sugg, - }, - }; - tcx.dcx().emit_err(err_struct) + diag.emit() } traits::OrphanCheckErr::UncoveredTy(param_ty, local_type) => { let mut sp = sp; diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index ee3436805ca7f..0b8ac9926e419 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -418,8 +418,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { { if let &hir::ClosureBinder::For { span: for_sp, .. } = binder { fn span_of_infer(ty: &hir::Ty<'_>) -> Option { - struct V; - impl<'v> Visitor<'v> for V { + /// Look for `_` anywhere in the signature of a `for<> ||` closure. + /// This is currently disallowed. + struct FindInferInClosureWithBinder; + impl<'v> Visitor<'v> for FindInferInClosureWithBinder { type Result = ControlFlow; fn visit_ty(&mut self, t: &'v hir::Ty<'v>) -> Self::Result { if matches!(t.kind, hir::TyKind::Infer) { @@ -429,7 +431,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { } } } - V.visit_ty(ty).break_value() + FindInferInClosureWithBinder.visit_ty(ty).break_value() } let infer_in_rt_sp = match fn_decl.output { diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 722def2563cbd..9d7deebac48e4 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -474,9 +474,9 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder { tcx.type_of(tcx.hir().get_parent_item(hir_id)).instantiate_identity() } - VariantData::Tuple(..) => { + VariantData::Tuple(_, _, ctor) => { let args = ty::GenericArgs::identity_for_item(tcx, def_id); - Ty::new_fn_def(tcx, def_id.to_def_id(), args) + Ty::new_fn_def(tcx, ctor.to_def_id(), args) } }, diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index fb919714afd38..2d4742fa1dc4a 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1376,7 +1376,7 @@ pub struct TyParamSome<'a> { } #[derive(Diagnostic)] -pub enum OnlyCurrentTraits<'a> { +pub enum OnlyCurrentTraits { #[diag(hir_analysis_only_current_traits_outside, code = E0117)] Outside { #[primary_span] @@ -1384,18 +1384,6 @@ pub enum OnlyCurrentTraits<'a> { span: Span, #[note(hir_analysis_only_current_traits_note)] note: (), - #[subdiagnostic] - opaque: Vec, - #[subdiagnostic] - foreign: Vec, - #[subdiagnostic] - name: Vec>, - #[subdiagnostic] - pointer: Vec>, - #[subdiagnostic] - ty: Vec>, - #[subdiagnostic] - sugg: Option>, }, #[diag(hir_analysis_only_current_traits_primitive, code = E0117)] Primitive { @@ -1404,18 +1392,6 @@ pub enum OnlyCurrentTraits<'a> { span: Span, #[note(hir_analysis_only_current_traits_note)] note: (), - #[subdiagnostic] - opaque: Vec, - #[subdiagnostic] - foreign: Vec, - #[subdiagnostic] - name: Vec>, - #[subdiagnostic] - pointer: Vec>, - #[subdiagnostic] - ty: Vec>, - #[subdiagnostic] - sugg: Option>, }, #[diag(hir_analysis_only_current_traits_arbitrary, code = E0117)] Arbitrary { @@ -1424,18 +1400,6 @@ pub enum OnlyCurrentTraits<'a> { span: Span, #[note(hir_analysis_only_current_traits_note)] note: (), - #[subdiagnostic] - opaque: Vec, - #[subdiagnostic] - foreign: Vec, - #[subdiagnostic] - name: Vec>, - #[subdiagnostic] - pointer: Vec>, - #[subdiagnostic] - ty: Vec>, - #[subdiagnostic] - sugg: Option>, }, } @@ -1445,7 +1409,6 @@ pub struct OnlyCurrentTraitsOpaque { #[primary_span] pub span: Span, } - #[derive(Subdiagnostic)] #[label(hir_analysis_only_current_traits_foreign)] pub struct OnlyCurrentTraitsForeign { @@ -1477,6 +1440,14 @@ pub struct OnlyCurrentTraitsTy<'a> { pub ty: Ty<'a>, } +#[derive(Subdiagnostic)] +#[label(hir_analysis_only_current_traits_adt)] +pub struct OnlyCurrentTraitsAdt { + #[primary_span] + pub span: Span, + pub name: String, +} + #[derive(Subdiagnostic)] #[multipart_suggestion( hir_analysis_only_current_traits_pointer_sugg, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index ca2e14ee3591e..70f09dd61758e 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -1,6 +1,6 @@ use crate::errors::{ self, AssocTypeBindingNotAllowed, ManualImplementation, MissingTypeParams, - ParenthesizedFnTraitExpansion, + ParenthesizedFnTraitExpansion, TraitObjectDeclaredWithNoTraits, }; use crate::fluent_generated as fluent; use crate::hir_ty_lowering::HirTyLowerer; @@ -8,19 +8,26 @@ use crate::traits::error_reporting::report_object_safety_error; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::unord::UnordMap; +use rustc_errors::MultiSpan; use rustc_errors::{ codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, }; use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::traits::FulfillmentError; use rustc_middle::query::Key; -use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, suggest_constraining_type_param}; +use rustc_middle::ty::{AdtDef, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{Binder, TraitRef}; use rustc_session::parse::feature_err; use rustc_span::edit_distance::find_best_match_for_name; -use rustc_span::symbol::{sym, Ident}; +use rustc_span::symbol::{kw, sym, Ident}; +use rustc_span::BytePos; use rustc_span::{Span, Symbol, DUMMY_SP}; -use rustc_trait_selection::traits::object_safety_violations_for_assoc_item; +use rustc_trait_selection::traits::{ + object_safety_violations_for_assoc_item, TraitAliasExpansionInfo, +}; impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// On missing type parameters, emit an E0393 error and provide a structured suggestion using @@ -621,7 +628,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let projection_ty = pred.skip_binder().projection_ty; let args_with_infer_self = tcx.mk_args_from_iter( - std::iter::once(Ty::new_var(tcx, ty::TyVid::from_u32(0)).into()) + std::iter::once(Ty::new_var(tcx, ty::TyVid::ZERO).into()) .chain(projection_ty.args.iter().skip(1)), ); @@ -1024,6 +1031,170 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Ok(()) } } + + pub fn report_prohibit_generics_error<'a>( + &self, + segments: impl Iterator> + Clone, + args_visitors: impl Iterator> + Clone, + err_extend: GenericsArgsErrExtend<'_>, + ) -> ErrorGuaranteed { + #[derive(PartialEq, Eq, Hash)] + enum ProhibitGenericsArg { + Lifetime, + Type, + Const, + Infer, + } + + let mut prohibit_args = FxIndexSet::default(); + args_visitors.for_each(|arg| { + match arg { + hir::GenericArg::Lifetime(_) => prohibit_args.insert(ProhibitGenericsArg::Lifetime), + hir::GenericArg::Type(_) => prohibit_args.insert(ProhibitGenericsArg::Type), + hir::GenericArg::Const(_) => prohibit_args.insert(ProhibitGenericsArg::Const), + hir::GenericArg::Infer(_) => prohibit_args.insert(ProhibitGenericsArg::Infer), + }; + }); + + let types_and_spans: Vec<_> = segments + .clone() + .flat_map(|segment| { + if segment.args().args.is_empty() { + None + } else { + Some(( + match segment.res { + hir::def::Res::PrimTy(ty) => { + format!("{} `{}`", segment.res.descr(), ty.name()) + } + hir::def::Res::Def(_, def_id) + if let Some(name) = self.tcx().opt_item_name(def_id) => + { + format!("{} `{name}`", segment.res.descr()) + } + hir::def::Res::Err => "this type".to_string(), + _ => segment.res.descr().to_string(), + }, + segment.ident.span, + )) + } + }) + .collect(); + let this_type = match &types_and_spans[..] { + [.., _, (last, _)] => format!( + "{} and {last}", + types_and_spans[..types_and_spans.len() - 1] + .iter() + .map(|(x, _)| x.as_str()) + .intersperse(", ") + .collect::() + ), + [(only, _)] => only.to_string(), + [] => "this type".to_string(), + }; + + let arg_spans: Vec = segments + .clone() + .flat_map(|segment| segment.args().args) + .map(|arg| arg.span()) + .collect(); + + let mut kinds = Vec::with_capacity(4); + prohibit_args.iter().for_each(|arg| match arg { + ProhibitGenericsArg::Lifetime => kinds.push("lifetime"), + ProhibitGenericsArg::Type => kinds.push("type"), + ProhibitGenericsArg::Const => kinds.push("const"), + ProhibitGenericsArg::Infer => kinds.push("generic"), + }); + + let (kind, s) = match kinds[..] { + [.., _, last] => ( + format!( + "{} and {last}", + kinds[..kinds.len() - 1] + .iter() + .map(|&x| x) + .intersperse(", ") + .collect::() + ), + "s", + ), + [only] => (only.to_string(), ""), + [] => unreachable!("expected at least one generic to prohibit"), + }; + let last_span = *arg_spans.last().unwrap(); + let span: MultiSpan = arg_spans.into(); + let mut err = struct_span_code_err!( + self.tcx().dcx(), + span, + E0109, + "{kind} arguments are not allowed on {this_type}", + ); + err.span_label(last_span, format!("{kind} argument{s} not allowed")); + for (what, span) in types_and_spans { + err.span_label(span, format!("not allowed on {what}")); + } + generics_args_err_extend(self.tcx(), segments.clone(), &mut err, err_extend); + let reported = err.emit(); + self.set_tainted_by_errors(reported); + reported + } + + pub fn report_trait_object_addition_traits_error( + &self, + regular_traits: &Vec>, + ) -> ErrorGuaranteed { + let tcx = self.tcx(); + let first_trait = ®ular_traits[0]; + let additional_trait = ®ular_traits[1]; + let mut err = struct_span_code_err!( + tcx.dcx(), + additional_trait.bottom().1, + E0225, + "only auto traits can be used as additional traits in a trait object" + ); + additional_trait.label_with_exp_info( + &mut err, + "additional non-auto trait", + "additional use", + ); + first_trait.label_with_exp_info(&mut err, "first non-auto trait", "first use"); + err.help(format!( + "consider creating a new trait with all of these as supertraits and using that \ + trait here instead: `trait NewTrait: {} {{}}`", + regular_traits + .iter() + // FIXME: This should `print_sugared`, but also needs to integrate projection bounds... + .map(|t| t.trait_ref().print_only_trait_path().to_string()) + .collect::>() + .join(" + "), + )); + err.note( + "auto-traits like `Send` and `Sync` are traits that have special properties; \ + for more information on them, visit \ + ", + ); + let reported = err.emit(); + self.set_tainted_by_errors(reported); + reported + } + + pub fn report_trait_object_with_no_traits_error( + &self, + span: Span, + trait_bounds: &Vec<(Binder<'tcx, TraitRef<'tcx>>, Span)>, + ) -> ErrorGuaranteed { + let tcx = self.tcx(); + let trait_alias_span = trait_bounds + .iter() + .map(|&(trait_ref, _)| trait_ref.def_id()) + .find(|&trait_ref| tcx.is_trait_alias(trait_ref)) + .map(|trait_ref| tcx.def_span(trait_ref)); + let reported = + tcx.dcx().emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span }); + self.set_tainted_by_errors(reported); + reported + } } /// Emits an error regarding forbidden type binding associations @@ -1031,7 +1202,7 @@ pub fn prohibit_assoc_item_binding( tcx: TyCtxt<'_>, span: Span, segment: Option<(&hir::PathSegment<'_>, Span)>, -) { +) -> ErrorGuaranteed { tcx.dcx().emit_err(AssocTypeBindingNotAllowed { span, fn_trait_expansion: if let Some((segment, span)) = segment @@ -1044,7 +1215,7 @@ pub fn prohibit_assoc_item_binding( } else { None }, - }); + }) } pub(crate) fn fn_trait_to_string( @@ -1099,3 +1270,208 @@ pub(crate) fn fn_trait_to_string( format!("{}<{}, Output={}>", trait_segment.ident, args, ret) } } + +/// Used for generics args error extend. +pub enum GenericsArgsErrExtend<'tcx> { + EnumVariant { + qself: &'tcx hir::Ty<'tcx>, + assoc_segment: &'tcx hir::PathSegment<'tcx>, + adt_def: AdtDef<'tcx>, + }, + OpaqueTy, + PrimTy(hir::PrimTy), + SelfTyAlias { + def_id: DefId, + span: Span, + }, + SelfTyParam(Span), + TyParam(DefId), + DefVariant, + None, +} + +fn generics_args_err_extend<'a>( + tcx: TyCtxt<'_>, + segments: impl Iterator> + Clone, + err: &mut Diag<'_>, + err_extend: GenericsArgsErrExtend<'_>, +) { + match err_extend { + GenericsArgsErrExtend::EnumVariant { qself, assoc_segment, adt_def } => { + err.note("enum variants can't have type parameters"); + let type_name = tcx.item_name(adt_def.did()); + let msg = format!( + "you might have meant to specify type parameters on enum \ + `{type_name}`" + ); + let Some(args) = assoc_segment.args else { + return; + }; + // Get the span of the generics args *including* the leading `::`. + // We do so by stretching args.span_ext to the left by 2. Earlier + // it was done based on the end of assoc segment but that sometimes + // led to impossible spans and caused issues like #116473 + let args_span = args.span_ext.with_lo(args.span_ext.lo() - BytePos(2)); + if tcx.generics_of(adt_def.did()).count() == 0 { + // FIXME(estebank): we could also verify that the arguments being + // work for the `enum`, instead of just looking if it takes *any*. + err.span_suggestion_verbose( + args_span, + format!("{type_name} doesn't have generic parameters"), + "", + Applicability::MachineApplicable, + ); + return; + } + let Ok(snippet) = tcx.sess.source_map().span_to_snippet(args_span) else { + err.note(msg); + return; + }; + let (qself_sugg_span, is_self) = + if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind { + // If the path segment already has type params, we want to overwrite + // them. + match &path.segments { + // `segment` is the previous to last element on the path, + // which would normally be the `enum` itself, while the last + // `_` `PathSegment` corresponds to the variant. + [ + .., + hir::PathSegment { + ident, args, res: Res::Def(DefKind::Enum, _), .. + }, + _, + ] => ( + // We need to include the `::` in `Type::Variant::` + // to point the span to `::`, not just ``. + ident + .span + .shrink_to_hi() + .to(args.map_or(ident.span.shrink_to_hi(), |a| a.span_ext)), + false, + ), + [segment] => { + ( + // We need to include the `::` in `Type::Variant::` + // to point the span to `::`, not just ``. + segment.ident.span.shrink_to_hi().to(segment + .args + .map_or(segment.ident.span.shrink_to_hi(), |a| a.span_ext)), + kw::SelfUpper == segment.ident.name, + ) + } + _ => { + err.note(msg); + return; + } + } + } else { + err.note(msg); + return; + }; + let suggestion = vec![ + if is_self { + // Account for people writing `Self::Variant::`, where + // `Self` is the enum, and suggest replacing `Self` with the + // appropriate type: `Type::::Variant`. + (qself.span, format!("{type_name}{snippet}")) + } else { + (qself_sugg_span, snippet) + }, + (args_span, String::new()), + ]; + err.multipart_suggestion_verbose(msg, suggestion, Applicability::MaybeIncorrect); + } + GenericsArgsErrExtend::PrimTy(prim_ty) => { + let name = prim_ty.name_str(); + for segment in segments { + if let Some(args) = segment.args { + err.span_suggestion_verbose( + segment.ident.span.shrink_to_hi().to(args.span_ext), + format!("primitive type `{name}` doesn't have generic parameters"), + "", + Applicability::MaybeIncorrect, + ); + } + } + } + GenericsArgsErrExtend::OpaqueTy => { + err.note("`impl Trait` types can't have type parameters"); + } + GenericsArgsErrExtend::DefVariant => { + err.note("enum variants can't have type parameters"); + } + GenericsArgsErrExtend::TyParam(def_id) => { + if let Some(span) = tcx.def_ident_span(def_id) { + let name = tcx.item_name(def_id); + err.span_note(span, format!("type parameter `{name}` defined here")); + } + } + GenericsArgsErrExtend::SelfTyParam(span) => { + err.span_suggestion_verbose( + span, + "the `Self` type doesn't accept type parameters", + "", + Applicability::MaybeIncorrect, + ); + } + GenericsArgsErrExtend::SelfTyAlias { def_id, span } => { + let ty = tcx.at(span).type_of(def_id).instantiate_identity(); + let span_of_impl = tcx.span_of_impl(def_id); + let def_id = match *ty.kind() { + ty::Adt(self_def, _) => self_def.did(), + _ => return, + }; + + let type_name = tcx.item_name(def_id); + let span_of_ty = tcx.def_ident_span(def_id); + let generics = tcx.generics_of(def_id).count(); + + let msg = format!("`Self` is of type `{ty}`"); + if let (Ok(i_sp), Some(t_sp)) = (span_of_impl, span_of_ty) { + let mut span: MultiSpan = vec![t_sp].into(); + span.push_span_label( + i_sp, + format!("`Self` is on type `{type_name}` in this `impl`"), + ); + let mut postfix = ""; + if generics == 0 { + postfix = ", which doesn't have generic parameters"; + } + span.push_span_label(t_sp, format!("`Self` corresponds to this type{postfix}")); + err.span_note(span, msg); + } else { + err.note(msg); + } + for segment in segments { + if let Some(args) = segment.args + && segment.ident.name == kw::SelfUpper + { + if generics == 0 { + // FIXME(estebank): we could also verify that the arguments being + // work for the `enum`, instead of just looking if it takes *any*. + err.span_suggestion_verbose( + segment.ident.span.shrink_to_hi().to(args.span_ext), + "the `Self` type doesn't accept type parameters", + "", + Applicability::MachineApplicable, + ); + return; + } else { + err.span_suggestion_verbose( + segment.ident.span, + format!( + "the `Self` type doesn't accept type parameters, use the \ + concrete type's name `{type_name}` instead if you want to \ + specify its type parameters" + ), + type_name, + 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 8886a78c6ecdc..f726f2a7b8908 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -14,7 +14,7 @@ //! trait references and bounds. mod bounds; -mod errors; +pub mod errors; pub mod generics; mod lint; mod object_safety; @@ -22,14 +22,14 @@ mod object_safety; use crate::bounds::Bounds; use crate::collect::HirPlaceholderCollector; use crate::errors::AmbiguousLifetimeBound; -use crate::hir_ty_lowering::errors::prohibit_assoc_item_binding; +use crate::hir_ty_lowering::errors::{prohibit_assoc_item_binding, GenericsArgsErrExtend}; use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args}; use crate::middle::resolve_bound_vars as rbv; use crate::require_c_abi_if_c_variadic; use rustc_ast::TraitObjectSyntax; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::{ - codes::*, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, FatalError, MultiSpan, + codes::*, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, FatalError, }; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; @@ -46,7 +46,7 @@ use rustc_middle::ty::{ use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::symbol::{kw, Ident, Symbol}; -use rustc_span::{sym, BytePos, Span, DUMMY_SP}; +use rustc_span::{sym, Span, DUMMY_SP}; use rustc_target::spec::abi; use rustc_trait_selection::traits::wf::object_region_bounds; use rustc_trait_selection::traits::{self, ObligationCtxt}; @@ -632,7 +632,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { trait_ref: &hir::TraitRef<'tcx>, self_ty: Ty<'tcx>, ) -> ty::TraitRef<'tcx> { - self.prohibit_generic_args(trait_ref.path.segments.split_last().unwrap().1.iter(), |_| {}); + let _ = self.prohibit_generic_args( + trait_ref.path.segments.split_last().unwrap().1.iter(), + GenericsArgsErrExtend::None, + ); self.lower_mono_trait_ref( trait_ref.path.span, @@ -681,7 +684,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let trait_def_id = trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()); let trait_segment = trait_ref.path.segments.last().unwrap(); - self.prohibit_generic_args(trait_ref.path.segments.split_last().unwrap().1.iter(), |_| {}); + let _ = self.prohibit_generic_args( + trait_ref.path.segments.split_last().unwrap().1.iter(), + GenericsArgsErrExtend::None, + ); self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, false); let (generic_args, arg_count) = self.lower_generic_args_of_path( @@ -995,8 +1001,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir_ref_id: hir::HirId, span: Span, qself_ty: Ty<'tcx>, - qself: &hir::Ty<'_>, - assoc_segment: &hir::PathSegment<'tcx>, + qself: &'tcx hir::Ty<'tcx>, + assoc_segment: &'tcx hir::PathSegment<'tcx>, permit_variants: bool, ) -> Result<(Ty<'tcx>, DefKind, DefId), ErrorGuaranteed> { debug!(%qself_ty, ?assoc_segment.ident); @@ -1020,99 +1026,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if let Some(variant_def) = variant_def { if permit_variants { tcx.check_stability(variant_def.def_id, Some(hir_ref_id), span, None); - self.prohibit_generic_args(slice::from_ref(assoc_segment).iter(), |err| { - err.note("enum variants can't have type parameters"); - let type_name = tcx.item_name(adt_def.did()); - let msg = format!( - "you might have meant to specify type parameters on enum \ - `{type_name}`" - ); - let Some(args) = assoc_segment.args else { - return; - }; - // Get the span of the generics args *including* the leading `::`. - // We do so by stretching args.span_ext to the left by 2. Earlier - // it was done based on the end of assoc segment but that sometimes - // led to impossible spans and caused issues like #116473 - let args_span = args.span_ext.with_lo(args.span_ext.lo() - BytePos(2)); - if tcx.generics_of(adt_def.did()).count() == 0 { - // FIXME(estebank): we could also verify that the arguments being - // work for the `enum`, instead of just looking if it takes *any*. - err.span_suggestion_verbose( - args_span, - format!("{type_name} doesn't have generic parameters"), - "", - Applicability::MachineApplicable, - ); - return; - } - let Ok(snippet) = tcx.sess.source_map().span_to_snippet(args_span) - else { - err.note(msg); - return; - }; - let (qself_sugg_span, is_self) = - if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = - &qself.kind - { - // If the path segment already has type params, we want to overwrite - // them. - match &path.segments { - // `segment` is the previous to last element on the path, - // which would normally be the `enum` itself, while the last - // `_` `PathSegment` corresponds to the variant. - [ - .., - hir::PathSegment { - ident, - args, - res: Res::Def(DefKind::Enum, _), - .. - }, - _, - ] => ( - // We need to include the `::` in `Type::Variant::` - // to point the span to `::`, not just ``. - ident.span.shrink_to_hi().to(args - .map_or(ident.span.shrink_to_hi(), |a| a.span_ext)), - false, - ), - [segment] => ( - // We need to include the `::` in `Type::Variant::` - // to point the span to `::`, not just ``. - segment.ident.span.shrink_to_hi().to(segment - .args - .map_or(segment.ident.span.shrink_to_hi(), |a| { - a.span_ext - })), - kw::SelfUpper == segment.ident.name, - ), - _ => { - err.note(msg); - return; - } - } - } else { - err.note(msg); - return; - }; - let suggestion = vec![ - if is_self { - // Account for people writing `Self::Variant::`, where - // `Self` is the enum, and suggest replacing `Self` with the - // appropriate type: `Type::::Variant`. - (qself.span, format!("{type_name}{snippet}")) - } else { - (qself_sugg_span, snippet) - }, - (args_span, String::new()), - ]; - err.multipart_suggestion_verbose( - msg, - suggestion, - Applicability::MaybeIncorrect, - ); - }); + let _ = self.prohibit_generic_args( + slice::from_ref(assoc_segment).iter(), + GenericsArgsErrExtend::EnumVariant { qself, assoc_segment, adt_def }, + ); return Ok((qself_ty, DefKind::Variant, variant_def.def_id)); } else { variant_resolution = Some(variant_def.def_id); @@ -1624,111 +1541,26 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { pub fn prohibit_generic_args<'a>( &self, segments: impl Iterator> + Clone, - extend: impl Fn(&mut Diag<'_>), - ) -> bool { - let args = segments.clone().flat_map(|segment| segment.args().args); - - let (lt, ty, ct, inf) = - args.clone().fold((false, false, false, false), |(lt, ty, ct, inf), arg| match arg { - hir::GenericArg::Lifetime(_) => (true, ty, ct, inf), - hir::GenericArg::Type(_) => (lt, true, ct, inf), - hir::GenericArg::Const(_) => (lt, ty, true, inf), - hir::GenericArg::Infer(_) => (lt, ty, ct, true), - }); - let mut emitted = false; - if lt || ty || ct || inf { - let types_and_spans: Vec<_> = segments - .clone() - .flat_map(|segment| { - if segment.args().args.is_empty() { - None - } else { - Some(( - match segment.res { - Res::PrimTy(ty) => { - format!("{} `{}`", segment.res.descr(), ty.name()) - } - Res::Def(_, def_id) - if let Some(name) = self.tcx().opt_item_name(def_id) => - { - format!("{} `{name}`", segment.res.descr()) - } - Res::Err => "this type".to_string(), - _ => segment.res.descr().to_string(), - }, - segment.ident.span, - )) - } - }) - .collect(); - let this_type = match &types_and_spans[..] { - [.., _, (last, _)] => format!( - "{} and {last}", - types_and_spans[..types_and_spans.len() - 1] - .iter() - .map(|(x, _)| x.as_str()) - .intersperse(", ") - .collect::() - ), - [(only, _)] => only.to_string(), - [] => "this type".to_string(), - }; - - let arg_spans: Vec = args.map(|arg| arg.span()).collect(); - - let mut kinds = Vec::with_capacity(4); - if lt { - kinds.push("lifetime"); - } - if ty { - kinds.push("type"); - } - if ct { - kinds.push("const"); - } - if inf { - kinds.push("generic"); - } - let (kind, s) = match kinds[..] { - [.., _, last] => ( - format!( - "{} and {last}", - kinds[..kinds.len() - 1] - .iter() - .map(|&x| x) - .intersperse(", ") - .collect::() - ), - "s", - ), - [only] => (only.to_string(), ""), - [] => unreachable!("expected at least one generic to prohibit"), - }; - let last_span = *arg_spans.last().unwrap(); - let span: MultiSpan = arg_spans.into(); - let mut err = struct_span_code_err!( - self.tcx().dcx(), - span, - E0109, - "{kind} arguments are not allowed on {this_type}", - ); - err.span_label(last_span, format!("{kind} argument{s} not allowed")); - for (what, span) in types_and_spans { - err.span_label(span, format!("not allowed on {what}")); - } - extend(&mut err); - self.set_tainted_by_errors(err.emit()); - emitted = true; + err_extend: GenericsArgsErrExtend<'_>, + ) -> Result<(), ErrorGuaranteed> { + let args_visitors = segments.clone().flat_map(|segment| segment.args().args); + let mut result = Ok(()); + if let Some(_) = args_visitors.clone().next() { + result = Err(self.report_prohibit_generics_error( + segments.clone(), + args_visitors, + err_extend, + )); } for segment in segments { // Only emit the first error to avoid overloading the user with error messages. if let Some(b) = segment.args().bindings.first() { - prohibit_assoc_item_binding(self.tcx(), b.span, None); - return true; + return Err(prohibit_assoc_item_binding(self.tcx(), b.span, None)); } } - emitted + + result } /// Probe path segments that are semantically allowed to have generic arguments. @@ -1893,9 +1725,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Check for desugared `impl Trait`. assert!(tcx.is_type_alias_impl_trait(did)); let item_segment = path.segments.split_last().unwrap(); - self.prohibit_generic_args(item_segment.1.iter(), |err| { - err.note("`impl Trait` types can't have type parameters"); - }); + let _ = self + .prohibit_generic_args(item_segment.1.iter(), GenericsArgsErrExtend::OpaqueTy); let args = self.lower_generic_args_of_path_segment(span, did, item_segment.0); Ty::new_opaque(tcx, did, args) } @@ -1908,7 +1739,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { did, ) => { assert_eq!(opt_self_ty, None); - self.prohibit_generic_args(path.segments.split_last().unwrap().1.iter(), |_| {}); + let _ = self.prohibit_generic_args( + path.segments.split_last().unwrap().1.iter(), + GenericsArgsErrExtend::None, + ); self.lower_path_segment(span, did, path.segments.last().unwrap()) } Res::Def(kind @ DefKind::Variant, def_id) if permit_variants => { @@ -1920,13 +1754,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.probe_generic_path_segments(path.segments, None, kind, def_id, span); let indices: FxHashSet<_> = generic_segments.iter().map(|GenericPathSegment(_, index)| index).collect(); - self.prohibit_generic_args( + let _ = self.prohibit_generic_args( path.segments.iter().enumerate().filter_map(|(index, seg)| { if !indices.contains(&index) { Some(seg) } else { None } }), - |err| { - err.note("enum variants can't have type parameters"); - }, + GenericsArgsErrExtend::DefVariant, ); let GenericPathSegment(def_id, index) = generic_segments.last().unwrap(); @@ -1934,27 +1766,25 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } Res::Def(DefKind::TyParam, def_id) => { assert_eq!(opt_self_ty, None); - self.prohibit_generic_args(path.segments.iter(), |err| { - if let Some(span) = tcx.def_ident_span(def_id) { - let name = tcx.item_name(def_id); - err.span_note(span, format!("type parameter `{name}` defined here")); - } - }); + let _ = self.prohibit_generic_args( + path.segments.iter(), + GenericsArgsErrExtend::TyParam(def_id), + ); self.lower_ty_param(hir_id) } Res::SelfTyParam { .. } => { // `Self` in trait or type alias. assert_eq!(opt_self_ty, None); - self.prohibit_generic_args(path.segments.iter(), |err| { + let _ = self.prohibit_generic_args( + path.segments.iter(), if let [hir::PathSegment { args: Some(args), ident, .. }] = &path.segments { - err.span_suggestion_verbose( + GenericsArgsErrExtend::SelfTyParam( ident.span.shrink_to_hi().to(args.span_ext), - "the `Self` type doesn't accept type parameters", - "", - Applicability::MaybeIncorrect, - ); - } - }); + ) + } else { + GenericsArgsErrExtend::None + }, + ); tcx.types.self_param } Res::SelfTyAlias { alias_to: def_id, forbid_generic, .. } => { @@ -1962,65 +1792,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { assert_eq!(opt_self_ty, None); // Try to evaluate any array length constants. let ty = tcx.at(span).type_of(def_id).instantiate_identity(); - let span_of_impl = tcx.span_of_impl(def_id); - self.prohibit_generic_args(path.segments.iter(), |err| { - let def_id = match *ty.kind() { - ty::Adt(self_def, _) => self_def.did(), - _ => return, - }; - - let type_name = tcx.item_name(def_id); - let span_of_ty = tcx.def_ident_span(def_id); - let generics = tcx.generics_of(def_id).count(); - - let msg = format!("`Self` is of type `{ty}`"); - if let (Ok(i_sp), Some(t_sp)) = (span_of_impl, span_of_ty) { - let mut span: MultiSpan = vec![t_sp].into(); - span.push_span_label( - i_sp, - format!("`Self` is on type `{type_name}` in this `impl`"), - ); - let mut postfix = ""; - if generics == 0 { - postfix = ", which doesn't have generic parameters"; - } - span.push_span_label( - t_sp, - format!("`Self` corresponds to this type{postfix}"), - ); - err.span_note(span, msg); - } else { - err.note(msg); - } - for segment in path.segments { - if let Some(args) = segment.args - && segment.ident.name == kw::SelfUpper - { - if generics == 0 { - // FIXME(estebank): we could also verify that the arguments being - // work for the `enum`, instead of just looking if it takes *any*. - err.span_suggestion_verbose( - segment.ident.span.shrink_to_hi().to(args.span_ext), - "the `Self` type doesn't accept type parameters", - "", - Applicability::MachineApplicable, - ); - return; - } else { - err.span_suggestion_verbose( - segment.ident.span, - format!( - "the `Self` type doesn't accept type parameters, use the \ - concrete type's name `{type_name}` instead if you want to \ - specify its type parameters" - ), - type_name, - Applicability::MaybeIncorrect, - ); - } - } - } - }); + let _ = self.prohibit_generic_args( + path.segments.iter(), + GenericsArgsErrExtend::SelfTyAlias { def_id, span }, + ); // HACK(min_const_generics): Forbid generic `Self` types // here as we can't easily do that during nameres. // @@ -2061,7 +1836,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } Res::Def(DefKind::AssocTy, def_id) => { debug_assert!(path.segments.len() >= 2); - self.prohibit_generic_args(path.segments[..path.segments.len() - 2].iter(), |_| {}); + let _ = self.prohibit_generic_args( + path.segments[..path.segments.len() - 2].iter(), + GenericsArgsErrExtend::None, + ); // HACK: until we support ``, assume all of them are. let constness = if tcx.has_attr(tcx.parent(def_id), sym::const_trait) { ty::BoundConstness::ConstIfConst @@ -2079,19 +1857,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } Res::PrimTy(prim_ty) => { assert_eq!(opt_self_ty, None); - self.prohibit_generic_args(path.segments.iter(), |err| { - let name = prim_ty.name_str(); - for segment in path.segments { - if let Some(args) = segment.args { - err.span_suggestion_verbose( - segment.ident.span.shrink_to_hi().to(args.span_ext), - format!("primitive type `{name}` doesn't have generic parameters"), - "", - Applicability::MaybeIncorrect, - ); - } - } - }); + let _ = self.prohibit_generic_args( + path.segments.iter(), + GenericsArgsErrExtend::PrimTy(prim_ty), + ); match prim_ty { hir::PrimTy::Bool => tcx.types.bool, hir::PrimTy::Char => tcx.types.char, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs index b5b3a9131c57f..97ba946b7e013 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs @@ -1,5 +1,4 @@ use crate::bounds::Bounds; -use crate::errors::TraitObjectDeclaredWithNoTraits; use crate::hir_ty_lowering::{GenericArgCountMismatch, GenericArgCountResult, OnlySelfBounds}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::{codes::*, struct_span_code_err}; @@ -7,9 +6,10 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::fold::BottomUpFolder; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_middle::ty::{DynKind, ToPredicate}; -use rustc_span::Span; +use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::traits::error_reporting::report_object_safety_error; use rustc_trait_selection::traits::{self, hir_ty_lowering_object_safety_violations}; @@ -86,47 +86,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) = expanded_traits.partition(|i| tcx.trait_is_auto(i.trait_ref().def_id())); if regular_traits.len() > 1 { - let first_trait = ®ular_traits[0]; - let additional_trait = ®ular_traits[1]; - let mut err = struct_span_code_err!( - tcx.dcx(), - additional_trait.bottom().1, - E0225, - "only auto traits can be used as additional traits in a trait object" - ); - additional_trait.label_with_exp_info( - &mut err, - "additional non-auto trait", - "additional use", - ); - first_trait.label_with_exp_info(&mut err, "first non-auto trait", "first use"); - err.help(format!( - "consider creating a new trait with all of these as supertraits and using that \ - trait here instead: `trait NewTrait: {} {{}}`", - regular_traits - .iter() - // FIXME: This should `print_sugared`, but also needs to integrate projection bounds... - .map(|t| t.trait_ref().print_only_trait_path().to_string()) - .collect::>() - .join(" + "), - )); - err.note( - "auto-traits like `Send` and `Sync` are traits that have special properties; \ - for more information on them, visit \ - ", - ); - self.set_tainted_by_errors(err.emit()); - } - - if regular_traits.is_empty() && auto_traits.is_empty() { - let trait_alias_span = trait_bounds - .iter() - .map(|&(trait_ref, _)| trait_ref.def_id()) - .find(|&trait_ref| tcx.is_trait_alias(trait_ref)) - .map(|trait_ref| tcx.def_span(trait_ref)); - let reported = - tcx.dcx().emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span }); - self.set_tainted_by_errors(reported); + let _ = self.report_trait_object_addition_traits_error(®ular_traits); + } else if regular_traits.is_empty() && auto_traits.is_empty() { + let reported = self.report_trait_object_with_no_traits_error(span, &trait_bounds); return Ty::new_error(tcx, reported); } @@ -267,12 +229,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if arg == dummy_self.into() { let param = &generics.params[index]; missing_type_params.push(param.name); - return Ty::new_misc_error(tcx).into(); + Ty::new_misc_error(tcx).into() } else if arg.walk().any(|arg| arg == dummy_self.into()) { references_self = true; - return Ty::new_misc_error(tcx).into(); + let guar = tcx.dcx().span_delayed_bug( + span, + "trait object trait bounds reference `Self`", + ); + replace_dummy_self_with_error(tcx, arg, guar) + } else { + arg } - arg }) .collect(); let args = tcx.mk_args(&args); @@ -327,18 +294,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let guar = tcx .dcx() .span_delayed_bug(span, "trait object projection bounds reference `Self`"); - let args: Vec<_> = b - .projection_ty - .args - .iter() - .map(|arg| { - if arg.walk().any(|arg| arg == dummy_self.into()) { - return Ty::new_error(tcx, guar).into(); - } - arg - }) - .collect(); - b.projection_ty.args = tcx.mk_args(&args); + b.projection_ty = replace_dummy_self_with_error(tcx, b.projection_ty, guar); } ty::ExistentialProjection::erase_self_ty(tcx, b) @@ -396,3 +352,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Ty::new_dynamic(tcx, existential_predicates, region_bound, representation) } } + +fn replace_dummy_self_with_error<'tcx, T: TypeFoldable>>( + tcx: TyCtxt<'tcx>, + t: T, + guar: ErrorGuaranteed, +) -> T { + t.fold_with(&mut BottomUpFolder { + tcx, + ty_op: |ty| { + if ty == tcx.types.trait_object_dummy_self { Ty::new_error(tcx, guar) } else { ty } + }, + lt_op: |lt| lt, + ct_op: |ct| ct, + }) +} diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 36f59b4ac2e01..bd528432e7046 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1721,12 +1721,15 @@ impl<'a> State<'a> { PatKind::Wild => self.word("_"), PatKind::Never => self.word("!"), PatKind::Binding(BindingAnnotation(by_ref, mutbl), _, ident, sub) => { - if by_ref == ByRef::Yes { - self.word_nbsp("ref"); - } if mutbl.is_mut() { self.word_nbsp("mut"); } + if let ByRef::Yes(rmutbl) = by_ref { + self.word_nbsp("ref"); + if rmutbl.is_mut() { + self.word_nbsp("mut"); + } + } self.print_ident(ident); if let Some(p) = sub { self.word("@"); diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index f7af438ad16c1..1d51101c94031 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -86,12 +86,12 @@ hir_typeck_invalid_callee = expected function, found {$ty} hir_typeck_lossy_provenance_int2ptr = strict provenance disallows casting integer `{$expr_ty}` to pointer `{$cast_ty}` .suggestion = use `.with_addr()` to adjust a valid pointer in the same allocation, to this address - .help = if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::from_exposed_addr()` instead + .help = if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead hir_typeck_lossy_provenance_ptr2int = under strict provenance it is considered bad style to cast pointer `{$expr_ty}` to integer `{$cast_ty}` .suggestion = use `.addr()` to obtain the address of a pointer - .help = if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_addr()` instead + .help = if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead hir_typeck_method_call_on_unknown_raw_pointee = cannot call a method on a raw pointer with an unknown pointee type diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 0e75a47683dcc..aa94632b2b077 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -918,7 +918,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let param = callee_args.const_at(host_effect_index); let cause = self.misc(span); - match self.at(&cause, self.param_env).eq(infer::DefineOpaqueTypes::No, effect, param) { + // We know the type of `effect` to be `bool`, there will be no opaque type inference. + match self.at(&cause, self.param_env).eq(infer::DefineOpaqueTypes::Yes, effect, param) { Ok(infer::InferOk { obligations, value: () }) => { self.register_predicates(obligations); } diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index 5841392dbcf16..59a043d1d6996 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -182,7 +182,7 @@ fn check_panic_info_fn(tcx: TyCtxt<'_>, fn_id: LocalDefId, fn_sig: ty::FnSig<'_> ty::Region::new_bound( tcx, ty::INNERMOST, - ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon }, + ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BrAnon }, ), panic_info_ty, ); diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 36af539401565..dbae8bfb54249 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -227,11 +227,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { kind: TypeVariableOriginKind::ClosureSynthetic, span: expr_span, }); - let closure_kind_ty = self.next_ty_var(TypeVariableOrigin { - // FIXME(eddyb) distinguish closure kind inference variables from the rest. - kind: TypeVariableOriginKind::ClosureSynthetic, - span: expr_span, - }); + + let closure_kind_ty = match expected_kind { + Some(kind) => Ty::from_closure_kind(tcx, kind), + + // Create a type variable (for now) to represent the closure kind. + // It will be unified during the upvar inference phase (`upvar.rs`) + None => self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::ClosureSynthetic, + span: expr_span, + }), + }; + let coroutine_captures_by_ref_ty = self.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::ClosureSynthetic, span: expr_span, @@ -262,10 +269,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, ); - let coroutine_kind_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::ClosureSynthetic, - span: expr_span, - }); + let coroutine_kind_ty = match expected_kind { + Some(kind) => Ty::from_coroutine_closure_kind(tcx, kind), + + // Create a type variable (for now) to represent the closure kind. + // It will be unified during the upvar inference phase (`upvar.rs`) + None => self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::ClosureSynthetic, + span: expr_span, + }), + }; + let coroutine_upvars_ty = self.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::ClosureSynthetic, span: expr_span, diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 564de4ab9e74c..75a68f16cf18a 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -400,7 +400,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // what our ideal rcvr ty would look like. let _ = self .at(&ObligationCause::dummy(), self.param_env) - .eq(DefineOpaqueTypes::No, method.sig.inputs()[idx + 1], arg_ty) + .eq(DefineOpaqueTypes::Yes, method.sig.inputs()[idx + 1], arg_ty) .ok()?; self.select_obligations_where_possible(|errs| { // Yeet the errors, we're already reporting errors. @@ -479,7 +479,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .and_then(|method| { let _ = self .at(&ObligationCause::dummy(), self.param_env) - .eq(DefineOpaqueTypes::No, ideal_rcvr_ty, expected_ty) + .eq(DefineOpaqueTypes::Yes, ideal_rcvr_ty, expected_ty) .ok()?; Some(method) }); diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index d3e6eb124f72d..d8f62f7a2b645 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -44,10 +44,7 @@ use rustc_infer::infer::InferOk; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::ObligationCause; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; -use rustc_middle::ty::error::{ - ExpectedFound, - TypeError::{FieldMisMatch, Sorts}, -}; +use rustc_middle::ty::error::{ExpectedFound, TypeError::Sorts}; use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{self, AdtKind, Ty, TypeVisitableExt}; use rustc_session::errors::ExprParenthesesNeeded; @@ -1811,7 +1808,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let target_ty = self.field_ty(base_expr.span, f, args); let cause = self.misc(base_expr.span); match self.at(&cause, self.param_env).sup( - DefineOpaqueTypes::No, + // We're already using inference variables for any params, and don't allow converting + // between different structs, so there is no way this ever actually defines an opaque type. + // Thus choosing `Yes` is fine. + DefineOpaqueTypes::Yes, target_ty, fru_ty, ) { @@ -1819,16 +1819,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.register_predicates(obligations) } Err(_) => { - // This should never happen, since we're just subtyping the - // remaining_fields, but it's fine to emit this, I guess. - self.err_ctxt() - .report_mismatched_types( - &cause, - target_ty, - fru_ty, - FieldMisMatch(variant.name, ident.name), - ) - .emit(); + span_bug!( + cause.span(), + "subtyping remaining fields of type changing FRU failed: {target_ty} != {fru_ty}: {}::{}", + variant.name, + ident.name, + ); } } } diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 3b6accb92ae5d..5986b95966635 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -739,12 +739,12 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { // In a cases of pattern like `let pat = upvar`, don't use the span // of the pattern, as this just looks confusing, instead use the span // of the discriminant. - match bm { - ty::BindByReference(m) => { + match bm.0 { + hir::ByRef::Yes(m) => { let bk = ty::BorrowKind::from_mutbl(m); delegate.borrow(place, discr_place.hir_id, bk); } - ty::BindByValue(..) => { + hir::ByRef::No => { debug!("walk_pat binding consuming pat"); delegate_consume(mc, *delegate, place, discr_place.hir_id); } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 011607bacc6cd..85d04f7d1c41e 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -11,6 +11,7 @@ use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_hir::{ExprKind, GenericArg, Node, QPath}; +use rustc_hir_analysis::hir_ty_lowering::errors::GenericsArgsErrExtend; use rustc_hir_analysis::hir_ty_lowering::generics::{ check_generic_arg_count_for_call, lower_generic_args, }; @@ -460,7 +461,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { where T: TypeVisitable>, { - t.has_free_regions() || t.has_projections() || t.has_infer_types() + t.has_free_regions() || t.has_aliases() || t.has_infer_types() } pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> { @@ -1177,11 +1178,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let indices: FxHashSet<_> = generic_segments.iter().map(|GenericPathSegment(_, index)| index).collect(); - let generics_has_err = self.lowerer().prohibit_generic_args( + let generics_err = self.lowerer().prohibit_generic_args( segments.iter().enumerate().filter_map(|(index, seg)| { if !indices.contains(&index) || is_alias_variant_ctor { Some(seg) } else { None } }), - |_| {}, + GenericsArgsErrExtend::None, ); if let Res::Local(hid) = res { @@ -1191,7 +1192,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return (ty, res); } - if generics_has_err { + if let Err(_) = generics_err { // Don't try to infer type parameters when prohibited generic arguments were given. user_self_ty = None; } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index ebc5e11a561d4..64b816553dff9 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -17,7 +17,8 @@ use itertools::Itertools; use rustc_ast as ast; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{ - codes::*, pluralize, Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey, + a_or_an, codes::*, display_list_with_comma_and, pluralize, Applicability, Diag, + ErrorGuaranteed, MultiSpan, StashKey, }; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; @@ -686,7 +687,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Using probe here, since we don't want this subtyping to affect inference. let subtyping_error = self.probe(|_| { self.at(&self.misc(arg_span), self.param_env) - .sup(DefineOpaqueTypes::No, formal_input_ty, coerced_ty) + .sup(DefineOpaqueTypes::Yes, formal_input_ty, coerced_ty) .err() }); @@ -818,6 +819,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr, None, Some(mismatch_idx), + &matched_inputs, + &formal_and_expected_inputs, is_method, ); suggest_confusable(&mut err); @@ -904,6 +907,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); err.span_label(full_call_span, format!("arguments to this {call_name} are incorrect")); + self.label_generic_mismatches( + &mut err, + fn_def_id, + &matched_inputs, + &provided_arg_tys, + &formal_and_expected_inputs, + is_method, + ); + if let hir::ExprKind::MethodCall(_, rcvr, _, _) = call_expr.kind && provided_idx.as_usize() == expected_idx.as_usize() { @@ -932,6 +944,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr, Some(expected_ty), Some(expected_idx.as_usize()), + &matched_inputs, + &formal_and_expected_inputs, is_method, ); suggest_confusable(&mut err); @@ -1270,6 +1284,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + self.label_generic_mismatches( + &mut err, + fn_def_id, + &matched_inputs, + &provided_arg_tys, + &formal_and_expected_inputs, + is_method, + ); + // Incorporate the argument changes in the removal suggestion. // When a type is *missing*, and the rest are additional, we want to suggest these with a // multipart suggestion, but in order to do so we need to figure out *where* the arg that @@ -1317,7 +1340,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // Call out where the function is defined - self.label_fn_like(&mut err, fn_def_id, callee_ty, call_expr, None, None, is_method); + self.label_fn_like( + &mut err, + fn_def_id, + callee_ty, + call_expr, + None, + None, + &matched_inputs, + &formal_and_expected_inputs, + is_method, + ); // And add a suggestion block for all of the parameters let suggestion_text = match suggestion_text { @@ -1916,29 +1949,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat: &'tcx hir::Pat<'tcx>, ty: Ty<'tcx>, ) { - struct V<'tcx> { - tcx: TyCtxt<'tcx>, - pat_hir_ids: Vec, - } - - impl<'tcx> Visitor<'tcx> for V<'tcx> { - type NestedFilter = rustc_middle::hir::nested_filter::All; - - fn nested_visit_map(&mut self) -> Self::Map { - self.tcx.hir() + if let Err(guar) = ty.error_reported() { + struct OverwritePatternsWithError { + pat_hir_ids: Vec, } - - fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) { - self.pat_hir_ids.push(p.hir_id); - hir::intravisit::walk_pat(self, p); + impl<'tcx> Visitor<'tcx> for OverwritePatternsWithError { + fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) { + self.pat_hir_ids.push(p.hir_id); + hir::intravisit::walk_pat(self, p); + } } - } - if let Err(guar) = ty.error_reported() { // Override the types everywhere with `err()` to avoid knock on errors. let err = Ty::new_error(self.tcx, guar); self.write_ty(hir_id, err); self.write_ty(pat.hir_id, err); - let mut visitor = V { tcx: self.tcx, pat_hir_ids: vec![] }; + let mut visitor = OverwritePatternsWithError { pat_hir_ids: vec![] }; hir::intravisit::walk_pat(&mut visitor, pat); // Mark all the subpatterns as `{type error}` as well. This allows errors for specific // subpatterns to be silenced. @@ -2102,6 +2127,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected_ty: Option>, // A specific argument should be labeled, instead of all of them expected_idx: Option, + matched_inputs: &IndexVec>, + formal_and_expected_inputs: &IndexVec, Ty<'tcx>)>, is_method: bool, ) { let Some(mut def_id) = callable_def_id else { @@ -2193,21 +2220,164 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let mut spans: MultiSpan = def_span.into(); - let params = self + let params_with_generics = self.get_hir_params_with_generics(def_id, is_method); + let mut generics_with_unmatched_params = Vec::new(); + + let check_for_matched_generics = || { + if matched_inputs.iter().any(|x| x.is_some()) + && params_with_generics.iter().any(|x| x.0.is_some()) + { + for (idx, (generic, _)) in params_with_generics.iter().enumerate() { + // Param has to have a generic and be matched to be relevant + if matched_inputs[idx.into()].is_none() { + continue; + } + + let Some(generic) = generic else { + continue; + }; + + for unmatching_idx in idx + 1..params_with_generics.len() { + if matched_inputs[unmatching_idx.into()].is_none() + && let Some(unmatched_idx_param_generic) = + params_with_generics[unmatching_idx].0 + && unmatched_idx_param_generic.name.ident() == generic.name.ident() + { + // We found a parameter that didn't match that needed to + return true; + } + } + } + } + false + }; + + let check_for_matched_generics = check_for_matched_generics(); + + for (idx, (generic_param, param)) in + params_with_generics.iter().enumerate().filter(|(idx, _)| { + check_for_matched_generics + || expected_idx.map_or(true, |expected_idx| expected_idx == *idx) + }) + { + let Some(generic_param) = generic_param else { + spans.push_span_label(param.span, ""); + continue; + }; + + let other_params_matched: Vec<(usize, &hir::Param<'_>)> = params_with_generics + .iter() + .enumerate() + .filter(|(other_idx, (other_generic_param, _))| { + if *other_idx == idx { + return false; + } + let Some(other_generic_param) = other_generic_param else { + return false; + }; + if matched_inputs[idx.into()].is_none() + && matched_inputs[(*other_idx).into()].is_none() + { + return false; + } + if matched_inputs[idx.into()].is_some() + && matched_inputs[(*other_idx).into()].is_some() + { + return false; + } + other_generic_param.name.ident() == generic_param.name.ident() + }) + .map(|(other_idx, (_, other_param))| (other_idx, *other_param)) + .collect(); + + if !other_params_matched.is_empty() { + let other_param_matched_names: Vec = other_params_matched + .iter() + .map(|(_, other_param)| { + if let hir::PatKind::Binding(_, _, ident, _) = other_param.pat.kind { + format!("`{ident}`") + } else { + "{unknown}".to_string() + } + }) + .collect(); + + let matched_ty = self + .resolve_vars_if_possible(formal_and_expected_inputs[idx.into()].1) + .sort_string(self.tcx); + + if matched_inputs[idx.into()].is_some() { + spans.push_span_label( + param.span, + format!( + "{} {} to match the {} type of this parameter", + display_list_with_comma_and(&other_param_matched_names), + format!( + "need{}", + pluralize!(if other_param_matched_names.len() == 1 { + 0 + } else { + 1 + }) + ), + matched_ty, + ), + ); + } else { + spans.push_span_label( + param.span, + format!( + "this parameter needs to match the {} type of {}", + matched_ty, + display_list_with_comma_and(&other_param_matched_names), + ), + ); + } + generics_with_unmatched_params.push(generic_param); + } else { + spans.push_span_label(param.span, ""); + } + } + + for generic_param in self .tcx .hir() .get_if_local(def_id) - .and_then(|node| node.body_id()) - .into_iter() - .flat_map(|id| self.tcx.hir().body(id).params) - .skip(if is_method { 1 } else { 0 }); - - for (_, param) in params + .and_then(|node| node.generics()) .into_iter() - .enumerate() - .filter(|(idx, _)| expected_idx.map_or(true, |expected_idx| expected_idx == *idx)) + .flat_map(|x| x.params) + .filter(|x| { + generics_with_unmatched_params.iter().any(|y| x.name.ident() == y.name.ident()) + }) { - spans.push_span_label(param.span, ""); + let param_idents_matching: Vec = params_with_generics + .iter() + .filter(|(generic, _)| { + if let Some(generic) = generic { + generic.name.ident() == generic_param.name.ident() + } else { + false + } + }) + .map(|(_, param)| { + if let hir::PatKind::Binding(_, _, ident, _) = param.pat.kind { + format!("`{ident}`") + } else { + "{unknown}".to_string() + } + }) + .collect(); + + if !param_idents_matching.is_empty() { + spans.push_span_label( + generic_param.span, + format!( + "{} all reference this parameter {}", + display_list_with_comma_and(¶m_idents_matching), + generic_param.name.ident().name, + ), + ); + } } err.span_note(spans, format!("{} defined here", self.tcx.def_descr(def_id))); @@ -2268,6 +2438,115 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } } + + fn label_generic_mismatches( + &self, + err: &mut Diag<'_>, + callable_def_id: Option, + matched_inputs: &IndexVec>, + provided_arg_tys: &IndexVec, Span)>, + formal_and_expected_inputs: &IndexVec, Ty<'tcx>)>, + is_method: bool, + ) { + let Some(def_id) = callable_def_id else { + return; + }; + + let params_with_generics = self.get_hir_params_with_generics(def_id, is_method); + + for (idx, (generic_param, _)) in params_with_generics.iter().enumerate() { + if matched_inputs[idx.into()].is_none() { + continue; + } + + let Some((_, matched_arg_span)) = provided_arg_tys.get(idx.into()) else { + continue; + }; + + let Some(generic_param) = generic_param else { + continue; + }; + + let mut idxs_matched: Vec = vec![]; + for (other_idx, (_, _)) in params_with_generics.iter().enumerate().filter( + |(other_idx, (other_generic_param, _))| { + if *other_idx == idx { + return false; + } + let Some(other_generic_param) = other_generic_param else { + return false; + }; + if matched_inputs[(*other_idx).into()].is_some() { + return false; + } + other_generic_param.name.ident() == generic_param.name.ident() + }, + ) { + idxs_matched.push(other_idx.into()); + } + + if idxs_matched.is_empty() { + continue; + } + + let expected_display_type = self + .resolve_vars_if_possible(formal_and_expected_inputs[idx.into()].1) + .sort_string(self.tcx); + let label = if idxs_matched.len() == params_with_generics.len() - 1 { + format!( + "expected all arguments to be this {} type because they need to match the type of this parameter", + expected_display_type + ) + } else { + format!( + "expected some other arguments to be {} {} type to match the type of this parameter", + a_or_an(&expected_display_type), + expected_display_type, + ) + }; + + err.span_label(*matched_arg_span, label); + } + } + + fn get_hir_params_with_generics( + &self, + def_id: DefId, + is_method: bool, + ) -> Vec<(Option<&hir::GenericParam<'_>>, &hir::Param<'_>)> { + let fn_node = self.tcx.hir().get_if_local(def_id); + + let generic_params: Vec>> = fn_node + .and_then(|node| node.fn_decl()) + .into_iter() + .flat_map(|decl| decl.inputs) + .skip(if is_method { 1 } else { 0 }) + .map(|param| { + if let hir::TyKind::Path(QPath::Resolved( + _, + hir::Path { res: Res::Def(_, res_def_id), .. }, + )) = param.kind + { + fn_node + .and_then(|node| node.generics()) + .into_iter() + .flat_map(|generics| generics.params) + .find(|gen| &gen.def_id.to_def_id() == res_def_id) + } else { + None + } + }) + .collect(); + + let params: Vec<&hir::Param<'_>> = fn_node + .and_then(|node| node.body_id()) + .into_iter() + .flat_map(|id| self.tcx.hir().body(id).params) + .skip(if is_method { 1 } else { 0 }) + .collect(); + + generic_params.into_iter().zip(params).collect() + } } struct FindClosureArg<'tcx> { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 74f27cfebbd22..05e7c5b2b4188 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -416,13 +416,13 @@ fn parse_never_type_options_attr( continue; } - if item.has_name(sym::diverging_block_default) && fallback.is_none() { - let mode = item.value_str().unwrap(); - match mode { + if item.has_name(sym::diverging_block_default) && block.is_none() { + let default = item.value_str().unwrap(); + match default { sym::unit => block = Some(DivergingBlockBehavior::Unit), sym::never => block = Some(DivergingBlockBehavior::Never), _ => { - tcx.dcx().span_err(item.span(), format!("unknown diverging block default: `{mode}` (supported: `unit` and `never`)")); + tcx.dcx().span_err(item.span(), format!("unknown diverging block default: `{default}` (supported: `unit` and `never`)")); } }; continue; @@ -431,7 +431,7 @@ fn parse_never_type_options_attr( tcx.dcx().span_err( item.span(), format!( - "unknown never type option: `{}` (supported: `fallback`)", + "unknown or duplicate never type option: `{}` (supported: `fallback`, `diverging_block_default`)", item.name_or_empty() ), ); diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs index 9e3867e630d48..62711e40049a4 100644 --- a/compiler/rustc_hir_typeck/src/intrinsicck.rs +++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs @@ -17,7 +17,7 @@ fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { let data_idx; let one = VariantIdx::new(1); - let zero = VariantIdx::new(0); + let zero = VariantIdx::ZERO; if def.variant(zero).fields.is_empty() { data_idx = one; diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs index f5b6dd162b393..f2425d034495a 100644 --- a/compiler/rustc_hir_typeck/src/mem_categorization.rs +++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs @@ -206,7 +206,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { .get(pat.hir_id) .expect("missing binding mode"); - if let ty::BindByReference(_) = bm { + if matches!(bm.0, hir::ByRef::Yes(_)) { // a bind-by-ref means that the base_ty will be the type of the ident itself, // but what we want here is the type of the underlying value being borrowed. // So peel off one-level, turning the &T into T. diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 12f522d1adcf0..a199f57aad99f 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -774,7 +774,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let projection_ty = pred.skip_binder().projection_ty; let args_with_infer_self = tcx.mk_args_from_iter( - iter::once(Ty::new_var(tcx, ty::TyVid::from_u32(0)).into()) + iter::once(Ty::new_var(tcx, ty::TyVid::ZERO).into()) .chain(projection_ty.args.iter().skip(1)), ); diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 861a00ce87453..bb47f8dfba46c 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -5,14 +5,13 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::{ codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, MultiSpan, }; -use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; -use rustc_hir::{HirId, Pat, PatKind}; +use rustc_hir::{self as hir, BindingAnnotation, ByRef, HirId, Mutability, Pat, PatKind}; use rustc_infer::infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::mir::interpret::ErrorHandled; -use rustc_middle::ty::{self, Adt, BindingMode, Ty, TypeVisitableExt}; +use rustc_middle::ty::{self, Adt, Ty, TypeVisitableExt}; use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::hygiene::DesugaringKind; @@ -79,7 +78,7 @@ struct TopInfo<'tcx> { #[derive(Copy, Clone)] struct PatInfo<'tcx, 'a> { - binding_mode: BindingMode, + binding_mode: BindingAnnotation, top_info: TopInfo<'tcx>, decl_origin: Option>, @@ -124,14 +123,21 @@ impl<'tcx> FnCtxt<'_, 'tcx> { } } -const INITIAL_BM: BindingMode = BindingMode::BindByValue(hir::Mutability::Not); +const INITIAL_BM: BindingAnnotation = BindingAnnotation(ByRef::No, Mutability::Not); /// Mode for adjusting the expected type and binding mode. enum AdjustMode { /// Peel off all immediate reference types. Peel, /// Reset binding mode to the initial mode. + /// Used for destructuring assignment, where we don't want any match ergonomics. Reset, + /// Produced by ref patterns. + /// Reset the binding mode to the initial mode, + /// and if the old biding mode was by-reference + /// with mutability matching the pattern, + /// mark the pattern as having consumed this reference. + ResetAndConsumeRef(Mutability), /// Pass on the input binding mode and expected type. Pass, } @@ -175,7 +181,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => None, }; let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res)); - let (expected, def_bm) = self.calc_default_binding_mode(pat, expected, def_bm, adjust_mode); + let (expected, def_bm, ref_pattern_already_consumed) = + self.calc_default_binding_mode(pat, expected, def_bm, adjust_mode); let pat_info = PatInfo { binding_mode: def_bm, top_info: ti, @@ -212,7 +219,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } PatKind::Box(inner) => self.check_pat_box(pat.span, inner, expected, pat_info), PatKind::Deref(inner) => self.check_pat_deref(pat.span, inner, expected, pat_info), - PatKind::Ref(inner, mutbl) => self.check_pat_ref(pat, inner, mutbl, expected, pat_info), + PatKind::Ref(inner, mutbl) => self.check_pat_ref( + pat, + inner, + mutbl, + expected, + pat_info, + ref_pattern_already_consumed, + ), PatKind::Slice(before, slice, after) => { self.check_pat_slice(pat.span, before, slice, after, expected, pat_info) } @@ -265,17 +279,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Compute the new expected type and default binding mode from the old ones /// as well as the pattern form we are currently checking. + /// + /// Last entry is only relevant for ref patterns (`&` and `&mut`); + /// if `true`, then the ref pattern consumed a match ergonomics inserted reference + /// and so does no need to match against a reference in the scrutinee type. fn calc_default_binding_mode( &self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, - def_bm: BindingMode, + def_bm: BindingAnnotation, adjust_mode: AdjustMode, - ) -> (Ty<'tcx>, BindingMode) { + ) -> (Ty<'tcx>, BindingAnnotation, bool) { match adjust_mode { - AdjustMode::Pass => (expected, def_bm), - AdjustMode::Reset => (expected, INITIAL_BM), - AdjustMode::Peel => self.peel_off_references(pat, expected, def_bm), + AdjustMode::Pass => (expected, def_bm, false), + AdjustMode::Reset => (expected, INITIAL_BM, false), + AdjustMode::ResetAndConsumeRef(mutbl) => { + (expected, INITIAL_BM, def_bm.0 == ByRef::Yes(mutbl)) + } + AdjustMode::Peel => { + let peeled = self.peel_off_references(pat, expected, def_bm); + (peeled.0, peeled.1, false) + } } } @@ -330,7 +354,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // ``` // // See issue #46688. - PatKind::Ref(..) => AdjustMode::Reset, + PatKind::Ref(_, mutbl) => AdjustMode::ResetAndConsumeRef(*mutbl), // A `_` pattern works with any expected type, so there's no need to do anything. PatKind::Wild // A malformed pattern doesn't have an expected type, so let's just accept any type. @@ -354,8 +378,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, - mut def_bm: BindingMode, - ) -> (Ty<'tcx>, BindingMode) { + mut def_bm: BindingAnnotation, + ) -> (Ty<'tcx>, BindingAnnotation) { let mut expected = self.try_structurally_resolve_type(pat.span, expected); // Peel off as many `&` or `&mut` from the scrutinee type as possible. For example, // for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches @@ -374,15 +398,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat_adjustments.push(expected); expected = self.try_structurally_resolve_type(pat.span, inner_ty); - def_bm = ty::BindByReference(match def_bm { + def_bm.0 = ByRef::Yes(match def_bm.0 { // If default binding mode is by value, make it `ref` or `ref mut` // (depending on whether we observe `&` or `&mut`). - ty::BindByValue(_) | + ByRef::No | // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`). - ty::BindByReference(hir::Mutability::Mut) => inner_mutability, + ByRef::Yes(Mutability::Mut) => inner_mutability, // Once a `ref`, always a `ref`. // This is because a `& &mut` cannot mutate the underlying value. - ty::BindByReference(m @ hir::Mutability::Not) => m, + ByRef::Yes(Mutability::Not) => Mutability::Not, }); } @@ -599,7 +623,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_pat_ident( &self, pat: &'tcx Pat<'tcx>, - ba: hir::BindingAnnotation, + ba: BindingAnnotation, var_id: HirId, sub: Option<&'tcx Pat<'tcx>>, expected: Ty<'tcx>, @@ -609,8 +633,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Determine the binding mode... let bm = match ba { - hir::BindingAnnotation::NONE => def_bm, - _ => BindingMode::convert(ba), + BindingAnnotation(ByRef::No, Mutability::Not) => def_bm, + _ => ba, }; // ...and store it in a side table: self.typeck_results.borrow_mut().pat_binding_modes_mut().insert(pat.hir_id, bm); @@ -618,8 +642,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("check_pat_ident: pat.hir_id={:?} bm={:?}", pat.hir_id, bm); let local_ty = self.local_ty(pat.span, pat.hir_id); - let eq_ty = match bm { - ty::BindByReference(mutbl) => { + let eq_ty = match bm.0 { + ByRef::Yes(mutbl) => { // If the binding is like `ref x | ref mut x`, // then `x` is assigned a value of type `&M T` where M is the // mutability and T is the expected type. @@ -630,10 +654,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.new_ref_ty(pat.span, mutbl, expected) } // Otherwise, the type of x is the expected type `T`. - ty::BindByValue(_) => { - // As above, `T <: typeof(x)` is required, but we use equality, see (note_1). - expected - } + ByRef::No => expected, // As above, `T <: typeof(x)` is required, but we use equality, see (note_1). }; self.demand_eqtype_pat(pat.span, eq_ty, local_ty, ti); @@ -655,7 +676,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// bindings have the same type by comparing them all against the type of that first pat. fn check_binding_alt_eq_ty( &self, - ba: hir::BindingAnnotation, + ba: BindingAnnotation, span: Span, var_id: HirId, ty: Ty<'tcx>, @@ -695,10 +716,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, expected: Ty<'tcx>, actual: Ty<'tcx>, - ba: hir::BindingAnnotation, + ba: BindingAnnotation, ) { match (expected.kind(), actual.kind(), ba) { - (ty::Ref(_, inner_ty, _), _, hir::BindingAnnotation::NONE) + (ty::Ref(_, inner_ty, _), _, BindingAnnotation::NONE) if self.can_eq(self.param_env, *inner_ty, actual) => { err.span_suggestion_verbose( @@ -708,7 +729,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MaybeIncorrect, ); } - (_, ty::Ref(_, inner_ty, _), hir::BindingAnnotation::REF) + (_, ty::Ref(_, inner_ty, _), BindingAnnotation::REF) if self.can_eq(self.param_env, expected, *inner_ty) => { err.span_suggestion_verbose( @@ -800,7 +821,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let PatKind::Ref(the_ref, _) = i.kind && let PatKind::Binding(mt, _, ident, _) = the_ref.kind { - let hir::BindingAnnotation(_, mtblty) = mt; + let BindingAnnotation(_, mtblty) = mt; err.span_suggestion_verbose( i.span, format!("consider removing `&{mutability}` from the pattern"), @@ -844,8 +865,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let Some(mt) = self.shallow_resolve(expected).builtin_deref(true) && let ty::Dynamic(..) = mt.ty.kind() { - // This is "x = SomeTrait" being reduced from - // "let &x = &SomeTrait" or "let box x = Box", an error. + // This is "x = dyn SomeTrait" being reduced from + // "let &x = &dyn SomeTrait" or "let box x = Box", an error. let type_str = self.ty_to_string(expected); let mut err = struct_span_code_err!( self.dcx(), @@ -2037,9 +2058,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, pat: &'tcx Pat<'tcx>, inner: &'tcx Pat<'tcx>, - mutbl: hir::Mutability, + mutbl: Mutability, expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>, + consumed_inherited_ref: bool, ) -> Ty<'tcx> { let tcx = self.tcx; let expected = self.shallow_resolve(expected); @@ -2055,26 +2077,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match *expected.kind() { ty::Ref(_, r_ty, r_mutbl) if r_mutbl == mutbl => (expected, r_ty), _ => { - let inner_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span: inner.span, - }); - let ref_ty = self.new_ref_ty(pat.span, mutbl, inner_ty); - debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty); - let err = self.demand_eqtype_pat_diag( - pat.span, - expected, - ref_ty, - pat_info.top_info, - ); + if consumed_inherited_ref && self.tcx.features().ref_pat_everywhere { + // We already matched against a match-ergonmics inserted reference, + // so we don't need to match against a reference from the original type. + // Save this infor for use in lowering later + self.typeck_results + .borrow_mut() + .skipped_ref_pats_mut() + .insert(pat.hir_id); + (expected, expected) + } else { + let inner_ty = self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span: inner.span, + }); + let ref_ty = self.new_ref_ty(pat.span, mutbl, inner_ty); + debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty); + let err = self.demand_eqtype_pat_diag( + pat.span, + expected, + ref_ty, + pat_info.top_info, + ); - // Look for a case like `fn foo(&foo: u32)` and suggest - // `fn foo(foo: &u32)` - if let Some(mut err) = err { - self.borrow_pat_suggestion(&mut err, pat); - err.emit(); + // Look for a case like `fn foo(&foo: u32)` and suggest + // `fn foo(foo: &u32)` + if let Some(mut err) = err { + self.borrow_pat_suggestion(&mut err, pat); + err.emit(); + } + (ref_ty, inner_ty) } - (ref_ty, inner_ty) } } } @@ -2088,7 +2121,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Create a reference type with a fresh region variable. - fn new_ref_ty(&self, span: Span, mutbl: hir::Mutability, ty: Ty<'tcx>) -> Ty<'tcx> { + fn new_ref_ty(&self, span: Span, mutbl: Mutability, ty: Ty<'tcx>) -> Ty<'tcx> { let region = self.next_region_var(infer::PatternRegion(span)); Ty::new_ref(self.tcx, region, ty, mutbl) } diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index e489b431e8184..c987bfb9a0e88 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -166,7 +166,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, body_id: hir::BodyId, body: &'tcx hir::Body<'tcx>, - capture_clause: hir::CaptureBy, + mut capture_clause: hir::CaptureBy, ) { // Extract the type of the closure. let ty = self.node_ty(closure_hir_id); @@ -259,6 +259,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) .consume_body(body); + // If a coroutine is comes from a coroutine-closure that is `move`, but + // the coroutine-closure was inferred to be `FnOnce` during signature + // inference, then it's still possible that we try to borrow upvars from + // the coroutine-closure because they are not used by the coroutine body + // in a way that forces a move. + // + // This would lead to an impossible to satisfy situation, since `AsyncFnOnce` + // coroutine bodies can't borrow from their parent closure. To fix this, + // we force the inner coroutine to also be `move`. This only matters for + // coroutine-closures that are `move` since otherwise they themselves will + // be borrowing from the outer environment, so there's no self-borrows occuring. + if let UpvarArgs::Coroutine(..) = args + && let hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Closure) = + self.tcx.coroutine_kind(closure_def_id).expect("coroutine should have kind") + && let parent_hir_id = + self.tcx.local_def_id_to_hir_id(self.tcx.local_parent(closure_def_id)) + && let parent_ty = self.node_ty(parent_hir_id) + && let Some(ty::ClosureKind::FnOnce) = self.closure_kind(parent_ty) + { + capture_clause = self.tcx.hir_node(parent_hir_id).expect_closure().capture_clause; + } + debug!( "For closure={:?}, capture_information={:#?}", closure_def_id, delegate.capture_information @@ -343,10 +365,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let closure_env_region: ty::Region<'_> = ty::Region::new_bound( self.tcx, ty::INNERMOST, - ty::BoundRegion { - var: ty::BoundVar::from_usize(0), - kind: ty::BoundRegionKind::BrEnv, - }, + ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BoundRegionKind::BrEnv }, ); let tupled_upvars_ty_for_borrow = Ty::new_tup_from_iter( self.tcx, @@ -402,16 +421,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); // Additionally, we can now constrain the coroutine's kind type. - let ty::Coroutine(_, coroutine_args) = - *self.typeck_results.borrow().expr_ty(body.value).kind() - else { - bug!(); - }; - self.demand_eqtype( - span, - coroutine_args.as_coroutine().kind_ty(), - Ty::from_coroutine_closure_kind(self.tcx, closure_kind), - ); + // + // We only do this if `infer_kind`, because if we have constrained + // the kind from closure signature inference, the kind inferred + // for the inner coroutine may actually be more restrictive. + if infer_kind { + let ty::Coroutine(_, coroutine_args) = + *self.typeck_results.borrow().expr_ty(body.value).kind() + else { + bug!(); + }; + self.demand_eqtype( + span, + coroutine_args.as_coroutine().kind_ty(), + Ty::from_coroutine_closure_kind(self.tcx, closure_kind), + ); + } } self.log_closure_min_capture_info(closure_def_id, span); @@ -1711,10 +1736,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let bm = *typeck_results.pat_binding_modes().get(var_hir_id).expect("missing binding mode"); - let mut is_mutbl = match bm { - ty::BindByValue(mutability) => mutability, - ty::BindByReference(_) => hir::Mutability::Not, - }; + let mut is_mutbl = bm.1; for pointer_ty in place.deref_tys() { match pointer_ty.kind() { diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index f4516b684c386..6604bf094c14a 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -345,6 +345,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { _ => {} }; + self.visit_skipped_ref_pats(p.hir_id); self.visit_pat_adjustments(p.span, p.hir_id); self.visit_node_id(p.span, p.hir_id); @@ -674,6 +675,14 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } + #[instrument(skip(self), level = "debug")] + fn visit_skipped_ref_pats(&mut self, hir_id: hir::HirId) { + if self.fcx.typeck_results.borrow_mut().skipped_ref_pats_mut().remove(hir_id) { + debug!("node is a skipped ref pat"); + self.typeck_results.skipped_ref_pats_mut().insert(hir_id); + } + } + fn visit_liberated_fn_sigs(&mut self) { let fcx_typeck_results = self.fcx.typeck_results.borrow(); assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner); diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index 357f2ae92d4c3..26aaa24771fe8 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -11,6 +11,7 @@ use rustc_session::config::IncrementalStateAssertion; use rustc_session::Session; use rustc_span::ErrorGuaranteed; use std::path::{Path, PathBuf}; +use std::sync::Arc; use super::data::*; use super::file_format; @@ -88,7 +89,7 @@ fn delete_dirty_work_product(sess: &Session, swp: SerializedWorkProduct) { work_product::delete_workproduct_files(sess, &swp.work_product); } -fn load_dep_graph(sess: &Session) -> LoadResult<(SerializedDepGraph, WorkProductMap)> { +fn load_dep_graph(sess: &Session) -> LoadResult<(Arc, WorkProductMap)> { let prof = sess.prof.clone(); if sess.opts.incremental.is_none() { diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index 32759f5284af7..9777f76928095 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -10,6 +10,7 @@ use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; use rustc_serialize::Encodable as RustcEncodable; use rustc_session::Session; use std::fs; +use std::sync::Arc; use super::data::*; use super::dirty_clean; @@ -147,7 +148,7 @@ fn encode_query_cache(tcx: TyCtxt<'_>, encoder: FileEncoder) -> FileEncodeResult /// and moves it to the permanent dep-graph path pub(crate) fn build_dep_graph( sess: &Session, - prev_graph: SerializedDepGraph, + prev_graph: Arc, prev_work_products: WorkProductMap, ) -> Option { if sess.opts.incremental.is_none() { diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index 12f8e42c78f94..c7e563035fc33 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -400,7 +400,7 @@ enum Chunk { } // This type is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] crate::static_assert_size!(Chunk, 16); impl ChunkedBitSet { diff --git a/compiler/rustc_index_macros/src/newtype.rs b/compiler/rustc_index_macros/src/newtype.rs index e5c2ba424834a..fe9a048734fc7 100644 --- a/compiler/rustc_index_macros/src/newtype.rs +++ b/compiler/rustc_index_macros/src/newtype.rs @@ -174,6 +174,9 @@ impl Parse for Newtype { /// Maximum value the index can take. #vis const MAX: Self = Self::from_u32(#max); + /// Zero value of the index. + #vis const ZERO: Self = Self::from_u32(0); + /// Creates a new index from a given `usize`. /// /// # Panics diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 339c8ac10b33a..6e5ed0a31cb10 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -483,7 +483,7 @@ pub enum SubregionOrigin<'tcx> { } // `SubregionOrigin` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] static_assert_size!(SubregionOrigin<'_>, 32); impl<'tcx> SubregionOrigin<'tcx> { @@ -843,7 +843,9 @@ impl<'tcx> InferCtxt<'tcx> { { let origin = &ObligationCause::dummy(); self.probe(|_| { - self.at(origin, param_env).sub(DefineOpaqueTypes::No, expected, actual).is_ok() + // We're only answering whether there could be a subtyping relation, and with + // opaque types, "there could be one", via registering a hidden type. + self.at(origin, param_env).sub(DefineOpaqueTypes::Yes, expected, actual).is_ok() }) } @@ -852,7 +854,9 @@ impl<'tcx> InferCtxt<'tcx> { T: at::ToTrace<'tcx>, { let origin = &ObligationCause::dummy(); - self.probe(|_| self.at(origin, param_env).eq(DefineOpaqueTypes::No, a, b).is_ok()) + // We're only answering whether the types could be the same, and with + // opaque types, "they can be the same", via registering a hidden type. + self.probe(|_| self.at(origin, param_env).eq(DefineOpaqueTypes::Yes, a, b).is_ok()) } #[instrument(skip(self), level = "debug")] diff --git a/compiler/rustc_infer/src/infer/snapshot/fudge.rs b/compiler/rustc_infer/src/infer/snapshot/fudge.rs index 14de461cd17eb..f8f1c1b4c45c1 100644 --- a/compiler/rustc_infer/src/infer/snapshot/fudge.rs +++ b/compiler/rustc_infer/src/infer/snapshot/fudge.rs @@ -13,7 +13,7 @@ use ut::UnifyKey; use std::ops::Range; fn vars_since_snapshot<'tcx, T>( - table: &mut UnificationTable<'_, 'tcx, T>, + table: &UnificationTable<'_, 'tcx, T>, snapshot_var_len: usize, ) -> Range where @@ -124,11 +124,11 @@ impl<'tcx> InferCtxt<'tcx> { let type_vars = inner.type_variables().vars_since_snapshot(variable_lengths.type_var_len); let int_vars = vars_since_snapshot( - &mut inner.int_unification_table(), + &inner.int_unification_table(), variable_lengths.int_var_len, ); let float_vars = vars_since_snapshot( - &mut inner.float_unification_table(), + &inner.float_unification_table(), variable_lengths.float_var_len, ); let region_vars = inner diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index ee9ce842d00af..0444cbe2ee413 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -32,7 +32,7 @@ #[macro_use] extern crate rustc_macros; -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] #[macro_use] extern crate rustc_data_structures; #[macro_use] diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index 616f5cc04564a..94ad0f5b1c863 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -112,7 +112,7 @@ impl<'tcx> PolyTraitObligation<'tcx> { } // `PredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] static_assert_size!(PredicateObligation<'_>, 48); pub type PredicateObligations<'tcx> = Vec>; diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index d763a12f816b0..1f92cc4d76399 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -748,9 +748,6 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { sess.time("MIR_effect_checking", || { for def_id in tcx.hir().body_owners() { - if !tcx.sess.opts.unstable_opts.thir_unsafeck { - rustc_mir_transform::check_unsafety::check_unsafety(tcx, def_id); - } tcx.ensure().has_ffi_unwind_calls(def_id); // If we need to codegen, ensure that we emit all errors from diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 3b78e6a43ab33..b9025917d1375 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -840,7 +840,6 @@ fn test_unstable_options_tracking_hash() { tracked!(stack_protector, StackProtector::All); tracked!(teach, true); tracked!(thinlto, Some(true)); - tracked!(thir_unsafeck, false); tracked!(tiny_const_eval_limit, true); tracked!(tls_model, Some(TlsModel::GeneralDynamic)); tracked!(translate_remapped_path_to_local_path, false); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 1dac2d89c6bc3..a034bebc85ec0 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1632,11 +1632,13 @@ pub struct AmbiguousWidePointerComparisonsAddrMetadataSuggestion<'a> { pub ne: &'a str, pub deref_left: &'a str, pub deref_right: &'a str, + pub l_modifiers: &'a str, + pub r_modifiers: &'a str, #[suggestion_part(code = "{ne}std::ptr::eq({deref_left}")] pub left: Span, - #[suggestion_part(code = ", {deref_right}")] + #[suggestion_part(code = "{l_modifiers}, {deref_right}")] pub middle: Span, - #[suggestion_part(code = ")")] + #[suggestion_part(code = "{r_modifiers})")] pub right: Span, } @@ -1652,11 +1654,13 @@ pub enum AmbiguousWidePointerComparisonsAddrSuggestion<'a> { ne: &'a str, deref_left: &'a str, deref_right: &'a str, + l_modifiers: &'a str, + r_modifiers: &'a str, #[suggestion_part(code = "{ne}std::ptr::addr_eq({deref_left}")] left: Span, - #[suggestion_part(code = ", {deref_right}")] + #[suggestion_part(code = "{l_modifiers}, {deref_right}")] middle: Span, - #[suggestion_part(code = ")")] + #[suggestion_part(code = "{r_modifiers})")] right: Span, }, #[multipart_suggestion( @@ -1668,14 +1672,18 @@ pub enum AmbiguousWidePointerComparisonsAddrSuggestion<'a> { Cast { deref_left: &'a str, deref_right: &'a str, - #[suggestion_part(code = "{deref_left}")] + paren_left: &'a str, + paren_right: &'a str, + l_modifiers: &'a str, + r_modifiers: &'a str, + #[suggestion_part(code = "({deref_left}")] left_before: Option, - #[suggestion_part(code = " as *const ()")] - left: Span, - #[suggestion_part(code = "{deref_right}")] + #[suggestion_part(code = "{l_modifiers}{paren_left}.cast::<()>()")] + left_after: Span, + #[suggestion_part(code = "({deref_right}")] right_before: Option, - #[suggestion_part(code = " as *const ()")] - right: Span, + #[suggestion_part(code = "{r_modifiers}{paren_right}.cast::<()>()")] + right_after: Span, }, } diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 5331d2fb752de..534eb60eeb001 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -657,14 +657,24 @@ fn lint_nan<'tcx>( cx.emit_span_lint(INVALID_NAN_COMPARISONS, e.span, lint); } +#[derive(Debug, PartialEq)] +enum ComparisonOp { + BinOp(hir::BinOpKind), + Other, +} + fn lint_wide_pointer<'tcx>( cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>, - binop: hir::BinOpKind, + cmpop: ComparisonOp, l: &'tcx hir::Expr<'tcx>, r: &'tcx hir::Expr<'tcx>, ) { - let ptr_unsized = |mut ty: Ty<'tcx>| -> Option<(usize, bool)> { + let ptr_unsized = |mut ty: Ty<'tcx>| -> Option<( + /* number of refs */ usize, + /* modifiers */ String, + /* is dyn */ bool, + )> { let mut refs = 0; // here we remove any "implicit" references and count the number // of them to correctly suggest the right number of deref @@ -672,14 +682,23 @@ fn lint_wide_pointer<'tcx>( ty = *inner_ty; refs += 1; } - match ty.kind() { - ty::RawPtr(ty, _) => (!ty.is_sized(cx.tcx, cx.param_env)) - .then(|| (refs, matches!(ty.kind(), ty::Dynamic(_, _, ty::Dyn)))), - _ => None, - } + + // get the inner type of a pointer (or akin) + let mut modifiers = String::new(); + ty = match ty.kind() { + ty::RawPtr(ty, _) => *ty, + ty::Adt(def, args) if cx.tcx.is_diagnostic_item(sym::NonNull, def.did()) => { + modifiers.push_str(".as_ptr()"); + args.type_at(0) + } + _ => return None, + }; + + (!ty.is_sized(cx.tcx, cx.param_env)) + .then(|| (refs, modifiers, matches!(ty.kind(), ty::Dynamic(_, _, ty::Dyn)))) }; - // PartialEq::{eq,ne} takes references, remove any explicit references + // the left and right operands can have references, remove any explicit references let l = l.peel_borrows(); let r = r.peel_borrows(); @@ -690,10 +709,10 @@ fn lint_wide_pointer<'tcx>( return; }; - let Some((l_ty_refs, l_inner_ty_is_dyn)) = ptr_unsized(l_ty) else { + let Some((l_ty_refs, l_modifiers, l_inner_ty_is_dyn)) = ptr_unsized(l_ty) else { return; }; - let Some((r_ty_refs, r_inner_ty_is_dyn)) = ptr_unsized(r_ty) else { + let Some((r_ty_refs, r_modifiers, r_inner_ty_is_dyn)) = ptr_unsized(r_ty) else { return; }; @@ -707,8 +726,8 @@ fn lint_wide_pointer<'tcx>( ); }; - let ne = if binop == hir::BinOpKind::Ne { "!" } else { "" }; - let is_eq_ne = matches!(binop, hir::BinOpKind::Eq | hir::BinOpKind::Ne); + let ne = if cmpop == ComparisonOp::BinOp(hir::BinOpKind::Ne) { "!" } else { "" }; + let is_eq_ne = matches!(cmpop, ComparisonOp::BinOp(hir::BinOpKind::Eq | hir::BinOpKind::Ne)); let is_dyn_comparison = l_inner_ty_is_dyn && r_inner_ty_is_dyn; let left = e.span.shrink_to_lo().until(l_span.shrink_to_lo()); @@ -718,6 +737,9 @@ fn lint_wide_pointer<'tcx>( let deref_left = &*"*".repeat(l_ty_refs); let deref_right = &*"*".repeat(r_ty_refs); + let l_modifiers = &*l_modifiers; + let r_modifiers = &*r_modifiers; + cx.emit_span_lint( AMBIGUOUS_WIDE_POINTER_COMPARISONS, e.span, @@ -727,6 +749,8 @@ fn lint_wide_pointer<'tcx>( ne, deref_left, deref_right, + l_modifiers, + r_modifiers, left, middle, right, @@ -737,6 +761,8 @@ fn lint_wide_pointer<'tcx>( ne, deref_left, deref_right, + l_modifiers, + r_modifiers, left, middle, right, @@ -745,12 +771,14 @@ fn lint_wide_pointer<'tcx>( AmbiguousWidePointerComparisonsAddrSuggestion::Cast { deref_left, deref_right, - // those two Options are required for correctness as having - // an empty span and an empty suggestion is not permitted - left_before: (l_ty_refs != 0).then_some(left), - right_before: (r_ty_refs != 0).then(|| r_span.shrink_to_lo()), - left: l_span.shrink_to_hi(), - right, + l_modifiers, + r_modifiers, + paren_left: if l_ty_refs != 0 { ")" } else { "" }, + paren_right: if r_ty_refs != 0 { ")" } else { "" }, + left_before: (l_ty_refs != 0).then_some(l_span.shrink_to_lo()), + left_after: l_span.shrink_to_hi(), + right_before: (r_ty_refs != 0).then_some(r_span.shrink_to_lo()), + right_after: r_span.shrink_to_hi(), } }, }, @@ -773,7 +801,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { cx.emit_span_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons); } else { lint_nan(cx, e, binop, l, r); - lint_wide_pointer(cx, e, binop.node, l, r); + lint_wide_pointer(cx, e, ComparisonOp::BinOp(binop.node), l, r); } } } @@ -782,16 +810,16 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { if let ExprKind::Path(ref qpath) = path.kind && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() && let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id) - && let Some(binop) = partialeq_binop(diag_item) => + && let Some(cmpop) = diag_item_cmpop(diag_item) => { - lint_wide_pointer(cx, e, binop, l, r); + lint_wide_pointer(cx, e, cmpop, l, r); } hir::ExprKind::MethodCall(_, l, [r], _) if let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) && let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id) - && let Some(binop) = partialeq_binop(diag_item) => + && let Some(cmpop) = diag_item_cmpop(diag_item) => { - lint_wide_pointer(cx, e, binop, l, r); + lint_wide_pointer(cx, e, cmpop, l, r); } _ => {} }; @@ -876,14 +904,20 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { ) } - fn partialeq_binop(diag_item: Symbol) -> Option { - if diag_item == sym::cmp_partialeq_eq { - Some(hir::BinOpKind::Eq) - } else if diag_item == sym::cmp_partialeq_ne { - Some(hir::BinOpKind::Ne) - } else { - None - } + fn diag_item_cmpop(diag_item: Symbol) -> Option { + Some(match diag_item { + sym::cmp_ord_max => ComparisonOp::Other, + sym::cmp_ord_min => ComparisonOp::Other, + sym::ord_cmp_method => ComparisonOp::Other, + sym::cmp_partialeq_eq => ComparisonOp::BinOp(hir::BinOpKind::Eq), + sym::cmp_partialeq_ne => ComparisonOp::BinOp(hir::BinOpKind::Ne), + sym::cmp_partialord_cmp => ComparisonOp::Other, + sym::cmp_partialord_ge => ComparisonOp::BinOp(hir::BinOpKind::Ge), + sym::cmp_partialord_gt => ComparisonOp::BinOp(hir::BinOpKind::Gt), + sym::cmp_partialord_le => ComparisonOp::BinOp(hir::BinOpKind::Le), + sym::cmp_partialord_lt => ComparisonOp::BinOp(hir::BinOpKind::Lt), + _ => return None, + }) } } } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 9f09f46ea5a9c..30522628f4680 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -2749,7 +2749,7 @@ declare_lint! { /// memory the pointer is allowed to read/write. Casting an integer, which /// doesn't have provenance, to a pointer requires the compiler to assign /// (guess) provenance. The compiler assigns "all exposed valid" (see the - /// docs of [`ptr::from_exposed_addr`] for more information about this + /// docs of [`ptr::with_exposed_provenance`] for more information about this /// "exposing"). This penalizes the optimiser and is not well suited for /// dynamic analysis/dynamic program verification (e.g. Miri or CHERI /// platforms). @@ -2757,11 +2757,11 @@ declare_lint! { /// It is much better to use [`ptr::with_addr`] instead to specify the /// provenance you want. If using this function is not possible because the /// code relies on exposed provenance then there is as an escape hatch - /// [`ptr::from_exposed_addr`]. + /// [`ptr::with_exposed_provenance`]. /// /// [issue #95228]: https://github.com/rust-lang/rust/issues/95228 /// [`ptr::with_addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.with_addr - /// [`ptr::from_exposed_addr`]: https://doc.rust-lang.org/core/ptr/fn.from_exposed_addr.html + /// [`ptr::with_exposed_provenance`]: https://doc.rust-lang.org/core/ptr/fn.with_exposed_provenance.html pub FUZZY_PROVENANCE_CASTS, Allow, "a fuzzy integer to pointer cast is used", @@ -2797,17 +2797,17 @@ declare_lint! { /// Since this cast is lossy, it is considered good style to use the /// [`ptr::addr`] method instead, which has a similar effect, but doesn't /// "expose" the pointer provenance. This improves optimisation potential. - /// See the docs of [`ptr::addr`] and [`ptr::expose_addr`] for more information + /// See the docs of [`ptr::addr`] and [`ptr::expose_provenance`] for more information /// about exposing pointer provenance. /// /// If your code can't comply with strict provenance and needs to expose - /// the provenance, then there is [`ptr::expose_addr`] as an escape hatch, + /// the provenance, then there is [`ptr::expose_provenance`] as an escape hatch, /// which preserves the behaviour of `as usize` casts while being explicit /// about the semantics. /// /// [issue #95228]: https://github.com/rust-lang/rust/issues/95228 /// [`ptr::addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.addr - /// [`ptr::expose_addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.expose_addr + /// [`ptr::expose_provenance`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.expose_provenance pub LOSSY_PROVENANCE_CASTS, Allow, "a lossy pointer to integer cast is used", diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index 4b0c1229da134..e2c0ec90c7cd3 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -391,6 +391,12 @@ fn main() { } } + // libc++abi and libunwind have to be specified explicitly on AIX. + if target.contains("aix") { + println!("cargo:rustc-link-lib=c++abi"); + println!("cargo:rustc-link-lib=unwind"); + } + // Libstdc++ depends on pthread which Rust doesn't link on MinGW // since nothing else requires it. if target.ends_with("windows-gnu") { diff --git a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp index 60789b07e54ea..0998b463a886c 100644 --- a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp @@ -1,8 +1,8 @@ #include "LLVMWrapper.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ProfileData/Coverage/CoverageMapping.h" #include "llvm/ProfileData/Coverage/CoverageMappingWriter.h" #include "llvm/ProfileData/InstrProf.h" -#include "llvm/ADT/ArrayRef.h" #include @@ -103,35 +103,30 @@ fromRust(LLVMRustCounterExprKind Kind) { } extern "C" void LLVMRustCoverageWriteFilenamesSectionToBuffer( - const char *const Filenames[], - size_t FilenamesLen, - const size_t *const Lengths, - size_t LengthsLen, + const char *const Filenames[], size_t FilenamesLen, // String start pointers + const size_t *const Lengths, size_t LengthsLen, // Corresponding lengths RustStringRef BufferOut) { if (FilenamesLen != LengthsLen) { report_fatal_error( "Mismatched lengths in LLVMRustCoverageWriteFilenamesSectionToBuffer"); } - SmallVector FilenameRefs; + SmallVector FilenameRefs; FilenameRefs.reserve(FilenamesLen); for (size_t i = 0; i < FilenamesLen; i++) { FilenameRefs.emplace_back(Filenames[i], Lengths[i]); } - auto FilenamesWriter = - coverage::CoverageFilenamesSectionWriter(ArrayRef(FilenameRefs)); + auto FilenamesWriter = coverage::CoverageFilenamesSectionWriter( + ArrayRef(FilenameRefs)); auto OS = RawRustStringOstream(BufferOut); FilenamesWriter.write(OS); } extern "C" void LLVMRustCoverageWriteMappingToBuffer( - const unsigned *VirtualFileMappingIDs, - unsigned NumVirtualFileMappingIDs, - const LLVMRustCounterExpression *RustExpressions, - unsigned NumExpressions, + const unsigned *VirtualFileMappingIDs, unsigned NumVirtualFileMappingIDs, + const LLVMRustCounterExpression *RustExpressions, unsigned NumExpressions, const LLVMRustCounterMappingRegion *RustMappingRegions, - unsigned NumMappingRegions, - RustStringRef BufferOut) { + unsigned NumMappingRegions, RustStringRef BufferOut) { // Convert from FFI representation to LLVM representation. SmallVector MappingRegions; MappingRegions.reserve(NumMappingRegions); @@ -142,7 +137,7 @@ extern "C" void LLVMRustCoverageWriteMappingToBuffer( #if LLVM_VERSION_GE(18, 0) && LLVM_VERSION_LT(19, 0) coverage::CounterMappingRegion::MCDCParameters{}, #endif - Region.FileID, Region.ExpandedFileID, + Region.FileID, Region.ExpandedFileID, // File IDs, then region info. Region.LineStart, Region.ColumnStart, Region.LineEnd, Region.ColumnEnd, fromRust(Region.Kind)); } @@ -158,29 +153,25 @@ extern "C" void LLVMRustCoverageWriteMappingToBuffer( auto CoverageMappingWriter = coverage::CoverageMappingWriter( ArrayRef(VirtualFileMappingIDs, NumVirtualFileMappingIDs), - Expressions, - MappingRegions); + Expressions, MappingRegions); auto OS = RawRustStringOstream(BufferOut); CoverageMappingWriter.write(OS); } -extern "C" LLVMValueRef LLVMRustCoverageCreatePGOFuncNameVar( - LLVMValueRef F, - const char *FuncName, - size_t FuncNameLen) { +extern "C" LLVMValueRef +LLVMRustCoverageCreatePGOFuncNameVar(LLVMValueRef F, const char *FuncName, + size_t FuncNameLen) { auto FuncNameRef = StringRef(FuncName, FuncNameLen); return wrap(createPGOFuncNameVar(*cast(unwrap(F)), FuncNameRef)); } -extern "C" uint64_t LLVMRustCoverageHashByteArray( - const char *Bytes, - size_t NumBytes) { +extern "C" uint64_t LLVMRustCoverageHashByteArray(const char *Bytes, + size_t NumBytes) { auto StrRef = StringRef(Bytes, NumBytes); return IndexedInstrProf::ComputeHash(StrRef); } -static void WriteSectionNameToString(LLVMModuleRef M, - InstrProfSectKind SK, +static void WriteSectionNameToString(LLVMModuleRef M, InstrProfSectKind SK, RustStringRef Str) { auto TargetTriple = Triple(unwrap(M)->getTargetTriple()); auto name = getInstrProfSectionName(SK, TargetTriple.getObjectFormat()); @@ -193,8 +184,9 @@ extern "C" void LLVMRustCoverageWriteMapSectionNameToString(LLVMModuleRef M, WriteSectionNameToString(M, IPSK_covmap, Str); } -extern "C" void LLVMRustCoverageWriteFuncSectionNameToString(LLVMModuleRef M, - RustStringRef Str) { +extern "C" void +LLVMRustCoverageWriteFuncSectionNameToString(LLVMModuleRef M, + RustStringRef Str) { WriteSectionNameToString(M, IPSK_covfun, Str); } @@ -205,5 +197,8 @@ extern "C" void LLVMRustCoverageWriteMappingVarNameToString(RustStringRef Str) { } extern "C" uint32_t LLVMRustCoverageMappingVersion() { - return coverage::CovMapVersion::Version6; + // This should always be `CurrentVersion`, because that's the version LLVM + // will use when encoding the data we give it. If for some reason we ever + // want to override the version number we _emit_, do it on the Rust side. + return coverage::CovMapVersion::CurrentVersion; } diff --git a/compiler/rustc_log/Cargo.toml b/compiler/rustc_log/Cargo.toml index 6009a43e98557..3ff86f700a535 100644 --- a/compiler/rustc_log/Cargo.toml +++ b/compiler/rustc_log/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" tracing = "0.1.28" tracing-core = "=0.1.30" # FIXME(Nilstrieb) tracing has a deadlock: https://github.com/tokio-rs/tracing/issues/2635 tracing-subscriber = { version = "0.3.3", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] } -tracing-tree = "0.2.0" +tracing-tree = "0.3.0" # tidy-alphabetical-end [dev-dependencies] diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 61060038b5006..7b10ea535248f 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -549,17 +549,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { match source_file.name { FileName::Real(ref original_file_name) => { - let adapted_file_name = if self.tcx.sess.should_prefer_remapped_for_codegen() { - source_map.path_mapping().to_embeddable_absolute_path( - original_file_name.clone(), - working_directory, - ) - } else { - source_map.path_mapping().to_local_embeddable_absolute_path( - original_file_name.clone(), - working_directory, - ) - }; + // FIXME: This should probably to conditionally remapped under + // a RemapPathScopeComponents but which one? + let adapted_file_name = source_map + .path_mapping() + .to_embeddable_absolute_path(original_file_name.clone(), working_directory); adapted_source_file.name = FileName::Real(adapted_file_name); } diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index c90427256b85f..bd11b3eb04c62 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -35,7 +35,6 @@ macro_rules! arena_types { )>, [] crate_for_resolver: rustc_data_structures::steal::Steal<(rustc_ast::Crate, rustc_ast::AttrVec)>, [] resolutions: rustc_middle::ty::ResolverGlobalCtxt, - [decode] unsafety_check_result: rustc_middle::mir::UnsafetyCheckResult, [decode] code_region: rustc_middle::mir::coverage::CodeRegion, [] const_allocs: rustc_middle::mir::interpret::Allocation, [] region_scope_tree: rustc_middle::middle::region::ScopeTree, diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index 39d82c489d53f..3c5bf6eb82461 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -194,9 +194,10 @@ impl DepNodeExt for DepNode { /// has been removed. fn extract_def_id(&self, tcx: TyCtxt<'_>) -> Option { if tcx.fingerprint_style(self.kind) == FingerprintStyle::DefPathHash { - Some(tcx.def_path_hash_to_def_id(DefPathHash(self.hash.into()), &mut || { - panic!("Failed to extract DefId: {:?} {}", self.kind, self.hash) - })) + Some(tcx.def_path_hash_to_def_id( + DefPathHash(self.hash.into()), + &("Failed to extract DefId", self.kind, self.hash), + )) } else { None } @@ -390,9 +391,10 @@ impl<'tcx> DepNodeParams> for HirId { let (local_hash, local_id) = Fingerprint::from(dep_node.hash).split(); let def_path_hash = DefPathHash::new(tcx.stable_crate_id(LOCAL_CRATE), local_hash); let def_id = tcx - .def_path_hash_to_def_id(def_path_hash, &mut || { - panic!("Failed to extract HirId: {:?} {}", dep_node.kind, dep_node.hash) - }) + .def_path_hash_to_def_id( + def_path_hash, + &("Failed to extract HirId", dep_node.kind, dep_node.hash), + ) .expect_local(); let local_id = local_id .as_u64() diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 53cb05198cd6f..72f849b534a81 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -13,7 +13,6 @@ use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId, LOCAL_CRATE}; use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; use rustc_hir::intravisit::Visitor; use rustc_hir::*; -use rustc_index::Idx; use rustc_middle::hir::nested_filter; use rustc_span::def_id::StableCrateId; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -69,7 +68,7 @@ impl<'hir> Iterator for ParentOwnerIterator<'hir> { fn next(&mut self) -> Option { if self.current_id.local_id.index() != 0 { - self.current_id.local_id = ItemLocalId::new(0); + self.current_id.local_id = ItemLocalId::ZERO; let node = self.map.tcx.hir_owner_node(self.current_id.owner); return Some((self.current_id.owner, node)); } @@ -133,7 +132,7 @@ impl<'tcx> TyCtxt<'tcx> { /// If calling repeatedly and iterating over parents, prefer [`Map::parent_iter`]. pub fn parent_hir_id(self, hir_id: HirId) -> HirId { let HirId { owner, local_id } = hir_id; - if local_id == ItemLocalId::from_u32(0) { + if local_id == ItemLocalId::ZERO { self.hir_owner_parent(owner) } else { let parent_local_id = self.hir_owner_nodes(owner).nodes[local_id].parent; diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 28f7574f66fc7..94d1039c763fa 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -174,8 +174,12 @@ pub fn provide(providers: &mut Providers) { let parent_owner_id = tcx.local_def_id_to_hir_id(parent_def_id).owner; HirId { owner: parent_owner_id, - local_id: tcx.hir_crate(()).owners[parent_owner_id.def_id].unwrap().parenting - [&owner_id.def_id], + local_id: tcx.hir_crate(()).owners[parent_owner_id.def_id] + .unwrap() + .parenting + .get(&owner_id.def_id) + .copied() + .unwrap_or(ItemLocalId::ZERO), } }) }; diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index 7b65c11bc3c82..acea89e4aabec 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -82,7 +82,7 @@ impl CanonicalVarValues<'_> { } pub fn is_identity_modulo_regions(&self) -> bool { - let mut var = ty::BoundVar::from_u32(0); + let mut var = ty::BoundVar::ZERO; for arg in self.var_values { match arg.unpack() { ty::GenericArgKind::Lifetime(r) => { diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 7d6d39a2a8a39..ec9697bbd3558 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -1,6 +1,7 @@ use crate::mir::mono::Linkage; use rustc_attr::{InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_span::symbol::Symbol; +use rustc_target::abi::Align; use rustc_target::spec::SanitizerSet; #[derive(Clone, TyEncodable, TyDecodable, HashStable, Debug)] @@ -42,7 +43,7 @@ pub struct CodegenFnAttrs { pub instruction_set: Option, /// The `#[repr(align(...))]` attribute. Indicates the value of which the function should be /// aligned to. - pub alignment: Option, + pub alignment: Option, } #[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)] diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs index 500536a9e9ed4..46520d69e1838 100644 --- a/compiler/rustc_middle/src/middle/privacy.rs +++ b/compiler/rustc_middle/src/middle/privacy.rs @@ -2,7 +2,7 @@ //! outside their scopes. This pass will also generate a set of exported items //! which are available for use externally when compiled as a library. use crate::ty::{TyCtxt, Visibility}; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxIndexMap, IndexEntry}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::def::DefKind; use rustc_macros::HashStable; @@ -90,7 +90,7 @@ impl EffectiveVisibility { /// Holds a map of effective visibilities for reachable HIR nodes. #[derive(Clone, Debug)] pub struct EffectiveVisibilities { - map: FxHashMap, + map: FxIndexMap, } impl EffectiveVisibilities { @@ -130,9 +130,8 @@ impl EffectiveVisibilities { eff_vis: &EffectiveVisibility, tcx: TyCtxt<'_>, ) { - use std::collections::hash_map::Entry; match self.map.entry(def_id) { - Entry::Occupied(mut occupied) => { + IndexEntry::Occupied(mut occupied) => { let old_eff_vis = occupied.get_mut(); for l in Level::all_levels() { let vis_at_level = eff_vis.at_level(l); @@ -145,7 +144,7 @@ impl EffectiveVisibilities { } old_eff_vis } - Entry::Vacant(vacant) => vacant.insert(*eff_vis), + IndexEntry::Vacant(vacant) => vacant.insert(*eff_vis), }; } diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 02185cbeacf9e..155af06201273 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -1,7 +1,7 @@ use std::fmt::{self, Debug, Display, Formatter}; use rustc_hir::def_id::DefId; -use rustc_session::RemapFileNameExt; +use rustc_session::{config::RemapPathScopeComponents, RemapFileNameExt}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{HasDataLayout, Size}; @@ -70,7 +70,7 @@ pub enum ConstValue<'tcx> { }, } -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] static_assert_size!(ConstValue<'_>, 24); impl<'tcx> ConstValue<'tcx> { @@ -516,7 +516,11 @@ impl<'tcx> TyCtxt<'tcx> { let caller = self.sess.source_map().lookup_char_pos(topmost.lo()); self.const_caller_location( rustc_span::symbol::Symbol::intern( - &caller.file.name.for_codegen(self.sess).to_string_lossy(), + &caller + .file + .name + .for_scope(self.sess, RemapPathScopeComponents::MACRO) + .to_string_lossy(), ), caller.line as u32, caller.col_display as u32 + 1, diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index 588aa1f40d79c..582a180668816 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -33,10 +33,6 @@ rustc_index::newtype_index! { pub struct CounterId {} } -impl CounterId { - pub const START: Self = Self::from_u32(0); -} - rustc_index::newtype_index! { /// ID of a coverage-counter expression. Values ascend from 0. /// @@ -55,10 +51,6 @@ rustc_index::newtype_index! { pub struct ExpressionId {} } -impl ExpressionId { - pub const START: Self = Self::from_u32(0); -} - /// Enum that can hold a constant zero value, the ID of an physical coverage /// counter, or the ID of a coverage-counter expression. /// diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index c86970635a55f..e9be26d058b48 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -88,7 +88,7 @@ pub type EvalToConstValueResult<'tcx> = Result, ErrorHandled>; /// This is needed in `thir::pattern::lower_inline_const`. pub type EvalToValTreeResult<'tcx> = Result>, ErrorHandled>; -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] static_assert_size!(InterpErrorInfo<'_>, 8); /// Packages the kind of error we got from the const code interpreter diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index 24d4a79c7d795..9f9433e483bc0 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -37,7 +37,7 @@ pub enum Scalar { Ptr(Pointer, u8), } -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] static_assert_size!(Scalar, 24); // We want the `Debug` output to be readable as it is used by `derive(Debug)` for diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 02af55fbf0e4f..ad166620bccf1 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -17,8 +17,10 @@ use rustc_data_structures::captures::Captures; use rustc_errors::{DiagArgName, DiagArgValue, DiagMessage, ErrorGuaranteed, IntoDiagArg}; use rustc_hir::def::{CtorKind, Namespace}; use rustc_hir::def_id::{DefId, CRATE_DEF_ID}; -use rustc_hir::{self, CoroutineDesugaring, CoroutineKind, ImplicitSelfKind}; -use rustc_hir::{self as hir, HirId}; +use rustc_hir::{ + self as hir, BindingAnnotation, ByRef, CoroutineDesugaring, CoroutineKind, HirId, + ImplicitSelfKind, +}; use rustc_session::Session; use rustc_span::source_map::Spanned; use rustc_target::abi::{FieldIdx, VariantIdx}; @@ -843,17 +845,6 @@ impl<'tcx> Body<'tcx> { } } -#[derive(Copy, Clone, PartialEq, Eq, Debug, TyEncodable, TyDecodable, HashStable)] -pub enum Safety { - Safe, - /// Unsafe because of compiler-generated unsafe code, like `await` desugaring - BuiltinUnsafe, - /// Unsafe because of an unsafe fn - FnUnsafe, - /// Unsafe because of an `unsafe` block - ExplicitUnsafe(hir::HirId), -} - impl<'tcx> Index for Body<'tcx> { type Output = BasicBlockData<'tcx>; @@ -992,8 +983,8 @@ pub enum LocalKind { #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] pub struct VarBindingForm<'tcx> { - /// Is variable bound via `x`, `mut x`, `ref x`, or `ref mut x`? - pub binding_mode: ty::BindingMode, + /// Is variable bound via `x`, `mut x`, `ref x`, `ref mut x`, `mut ref x`, or `mut ref mut x`? + pub binding_mode: BindingAnnotation, /// If an explicit type was provided for this variable binding, /// this holds the source Span of that type. /// @@ -1218,7 +1209,7 @@ impl<'tcx> LocalDecl<'tcx> { self.local_info(), LocalInfo::User( BindingForm::Var(VarBindingForm { - binding_mode: ty::BindingMode::BindByValue(_), + binding_mode: BindingAnnotation(ByRef::No, _), opt_ty_info: _, opt_match_place: _, pat_span: _, @@ -1235,7 +1226,7 @@ impl<'tcx> LocalDecl<'tcx> { self.local_info(), LocalInfo::User( BindingForm::Var(VarBindingForm { - binding_mode: ty::BindingMode::BindByValue(_), + binding_mode: BindingAnnotation(ByRef::No, _), opt_ty_info: _, opt_match_place: _, pat_span: _, @@ -1609,8 +1600,6 @@ pub struct SourceScopeData<'tcx> { pub struct SourceScopeLocalData { /// An `HirId` with lint levels equivalent to this scope's lint levels. pub lint_root: hir::HirId, - /// The unsafe block that contains this node. - pub safety: Safety, } /// A collection of projections into user types. @@ -1879,14 +1868,14 @@ impl DefLocation { } // Some nodes are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; // tidy-alphabetical-start static_assert_size!(BasicBlockData<'_>, 144); static_assert_size!(LocalDecl<'_>, 40); - static_assert_size!(SourceScopeData<'_>, 72); + static_assert_size!(SourceScopeData<'_>, 64); static_assert_size!(Statement<'_>, 32); static_assert_size!(StatementKind<'_>, 16); static_assert_size!(Terminator<'_>, 112); diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 731e050ca9b76..0f62212743086 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -3,9 +3,7 @@ use crate::mir; use crate::ty::{self, OpaqueHiddenType, Ty, TyCtxt}; use rustc_data_structures::fx::FxIndexMap; -use rustc_data_structures::unord::UnordSet; use rustc_errors::ErrorGuaranteed; -use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_index::bit_set::BitMatrix; use rustc_index::{Idx, IndexVec}; @@ -18,67 +16,6 @@ use std::fmt::{self, Debug}; use super::{ConstValue, SourceInfo}; -#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)] -pub enum UnsafetyViolationKind { - /// Unsafe operation outside `unsafe`. - General, - /// Unsafe operation in an `unsafe fn` but outside an `unsafe` block. - /// Has to be handled as a lint for backwards compatibility. - UnsafeFn, -} - -#[derive(Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)] -pub enum UnsafetyViolationDetails { - CallToUnsafeFunction, - UseOfInlineAssembly, - InitializingTypeWith, - CastOfPointerToInt, - UseOfMutableStatic, - UseOfExternStatic, - DerefOfRawPointer, - AccessToUnionField, - MutationOfLayoutConstrainedField, - BorrowOfLayoutConstrainedField, - CallToFunctionWith { - /// Target features enabled in callee's `#[target_feature]` but missing in - /// caller's `#[target_feature]`. - missing: Vec, - /// Target features in `missing` that are enabled at compile time - /// (e.g., with `-C target-feature`). - build_enabled: Vec, - }, -} - -#[derive(Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)] -pub struct UnsafetyViolation { - pub source_info: SourceInfo, - pub lint_root: hir::HirId, - pub kind: UnsafetyViolationKind, - pub details: UnsafetyViolationDetails, -} - -#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)] -pub enum UnusedUnsafe { - /// `unsafe` block contains no unsafe operations - /// > ``unnecessary `unsafe` block`` - Unused, - /// `unsafe` block nested under another (used) `unsafe` block - /// > ``… because it's nested under this `unsafe` block`` - InUnsafeBlock(hir::HirId), -} - -#[derive(TyEncodable, TyDecodable, HashStable, Debug)] -pub struct UnsafetyCheckResult { - /// Violations that are propagated *upwards* from this function. - pub violations: Vec, - - /// Used `unsafe` blocks in this function. This is used for the "unused_unsafe" lint. - pub used_unsafe_blocks: UnordSet, - - /// This is `Some` iff the item is not a closure. - pub unused_unsafes: Option>, -} - rustc_index::newtype_index! { #[derive(HashStable)] #[encodable] @@ -276,7 +213,7 @@ pub struct ClosureOutlivesRequirement<'tcx> { } // Make sure this enum doesn't unintentionally grow -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(ConstraintCategory<'_>, 16); /// Outlives-constraints can be categorized to determine whether and why they diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index f929a5cec2533..069c8019cb2c8 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -409,7 +409,7 @@ impl<'tcx> Rvalue<'tcx> { // Pointer to int casts may be side-effects due to exposing the provenance. // While the model is undecided, we should be conservative. See // - Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => false, + Rvalue::Cast(CastKind::PointerExposeProvenance, _, _) => false, Rvalue::Use(_) | Rvalue::CopyForDeref(_) @@ -426,7 +426,7 @@ impl<'tcx> Rvalue<'tcx> { | CastKind::FnPtrToPtr | CastKind::PtrToPtr | CastKind::PointerCoercion(_) - | CastKind::PointerFromExposedAddress + | CastKind::PointerWithExposedProvenance | CastKind::DynStar | CastKind::Transmute, _, diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 12bf8efd73ada..c78c225b0cdf8 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1309,11 +1309,11 @@ pub enum Rvalue<'tcx> { pub enum CastKind { /// An exposing pointer to address cast. A cast between a pointer and an integer type, or /// between a function pointer and an integer type. - /// See the docs on `expose_addr` for more details. - PointerExposeAddress, + /// See the docs on `expose_provenance` for more details. + PointerExposeProvenance, /// An address-to-pointer cast that picks up an exposed provenance. - /// See the docs on `from_exposed_addr` for more details. - PointerFromExposedAddress, + /// See the docs on `with_exposed_provenance` for more details. + PointerWithExposedProvenance, /// Pointer related casts that are done by coercions. Note that reference-to-raw-ptr casts are /// translated into `&raw mut/const *r`, i.e., they are not actually casts. PointerCoercion(PointerCoercion), @@ -1361,8 +1361,8 @@ pub enum NullOp<'tcx> { AlignOf, /// Returns the offset of a field OffsetOf(&'tcx List<(VariantIdx, FieldIdx)>), - /// Returns whether we want to check for UB. - /// This returns the value of `cfg!(debug_assertions)` at monomorphization time. + /// Returns whether we should perform some UB-checking at runtime. + /// See the `ub_checks` intrinsic docs for details. UbChecks, } @@ -1438,12 +1438,22 @@ pub enum BinOp { Ge, /// The `>` operator (greater than) Gt, + /// The `<=>` operator (three-way comparison, like `Ord::cmp`) + /// + /// This is supported only on the integer types and `char`, always returning + /// [`rustc_hir::LangItem::OrderingEnum`] (aka [`std::cmp::Ordering`]). + /// + /// [`Rvalue::BinaryOp`]`(BinOp::Cmp, A, B)` returns + /// - `Ordering::Less` (`-1_i8`, as a Scalar) if `A < B` + /// - `Ordering::Equal` (`0_i8`, as a Scalar) if `A == B` + /// - `Ordering::Greater` (`+1_i8`, as a Scalar) if `A > B` + Cmp, /// The `ptr.offset` operator Offset, } // Some nodes are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] mod size_asserts { use super::*; // tidy-alphabetical-start diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index 56a0a62339700..b86aa601ce8e3 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -14,7 +14,7 @@ pub struct PlaceTy<'tcx> { } // At least on 64 bit systems, `PlaceTy` should not be larger than two or three pointers. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] static_assert_size!(PlaceTy<'_>, 16); impl<'tcx> PlaceTy<'tcx> { @@ -276,6 +276,11 @@ impl<'tcx> BinOp { &BinOp::Eq | &BinOp::Lt | &BinOp::Le | &BinOp::Ne | &BinOp::Ge | &BinOp::Gt => { tcx.types.bool } + &BinOp::Cmp => { + // these should be integer-like types of the same size. + assert_eq!(lhs_ty, rhs_ty); + tcx.ty_ordering_enum(None) + } } } } @@ -312,7 +317,8 @@ impl BinOp { BinOp::Gt => hir::BinOpKind::Gt, BinOp::Le => hir::BinOpKind::Le, BinOp::Ge => hir::BinOpKind::Ge, - BinOp::AddUnchecked + BinOp::Cmp + | BinOp::AddUnchecked | BinOp::SubUnchecked | BinOp::MulUnchecked | BinOp::ShlUnchecked diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index 94f8cba6fb578..f116347cc2bad 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -149,44 +149,45 @@ impl AssertKind { matches!(self, OverflowNeg(..) | Overflow(Add | Sub | Mul | Shl | Shr, ..)) } - /// Get the message that is printed at runtime when this assertion fails. + /// Get the lang item that is invoked to print a static message when this assert fires. /// /// The caller is expected to handle `BoundsCheck` and `MisalignedPointerDereference` by /// invoking the appropriate lang item (panic_bounds_check/panic_misaligned_pointer_dereference) - /// instead of printing a static message. - pub fn description(&self) -> &'static str { + /// instead of printing a static message. Those have dynamic arguments that aren't present for + /// the rest of the messages here. + pub fn panic_function(&self) -> LangItem { use AssertKind::*; match self { - Overflow(BinOp::Add, _, _) => "attempt to add with overflow", - Overflow(BinOp::Sub, _, _) => "attempt to subtract with overflow", - Overflow(BinOp::Mul, _, _) => "attempt to multiply with overflow", - Overflow(BinOp::Div, _, _) => "attempt to divide with overflow", - Overflow(BinOp::Rem, _, _) => "attempt to calculate the remainder with overflow", - OverflowNeg(_) => "attempt to negate with overflow", - Overflow(BinOp::Shr, _, _) => "attempt to shift right with overflow", - Overflow(BinOp::Shl, _, _) => "attempt to shift left with overflow", + Overflow(BinOp::Add, _, _) => LangItem::PanicAddOverflow, + Overflow(BinOp::Sub, _, _) => LangItem::PanicSubOverflow, + Overflow(BinOp::Mul, _, _) => LangItem::PanicMulOverflow, + Overflow(BinOp::Div, _, _) => LangItem::PanicDivOverflow, + Overflow(BinOp::Rem, _, _) => LangItem::PanicRemOverflow, + OverflowNeg(_) => LangItem::PanicNegOverflow, + Overflow(BinOp::Shr, _, _) => LangItem::PanicShrOverflow, + Overflow(BinOp::Shl, _, _) => LangItem::PanicShlOverflow, Overflow(op, _, _) => bug!("{:?} cannot overflow", op), - DivisionByZero(_) => "attempt to divide by zero", - RemainderByZero(_) => "attempt to calculate the remainder with a divisor of zero", - ResumedAfterReturn(CoroutineKind::Coroutine(_)) => "coroutine resumed after completion", + DivisionByZero(_) => LangItem::PanicDivZero, + RemainderByZero(_) => LangItem::PanicRemZero, + ResumedAfterReturn(CoroutineKind::Coroutine(_)) => LangItem::PanicCoroutineResumed, ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => { - "`async fn` resumed after completion" + LangItem::PanicAsyncFnResumed } ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => { - "`async gen fn` resumed after completion" + LangItem::PanicAsyncGenFnResumed } ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => { - "`gen fn` should just keep returning `None` after completion" + LangItem::PanicGenFnNone } - ResumedAfterPanic(CoroutineKind::Coroutine(_)) => "coroutine resumed after panicking", + ResumedAfterPanic(CoroutineKind::Coroutine(_)) => LangItem::PanicCoroutineResumedPanic, ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => { - "`async fn` resumed after panicking" + LangItem::PanicAsyncFnResumedPanic } ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => { - "`async gen fn` resumed after panicking" + LangItem::PanicAsyncGenFnResumedPanic } ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => { - "`gen fn` should just keep returning `None` after panicking" + LangItem::PanicGenFnNonePanic } BoundsCheck { .. } | MisalignedPointerDereference { .. } => { @@ -198,7 +199,7 @@ impl AssertKind { /// Format the message arguments for the `assert(cond, msg..)` terminator in MIR printing. /// /// Needs to be kept in sync with the run-time behavior (which is defined by - /// `AssertKind::description` and the lang items mentioned in its docs). + /// `AssertKind::panic_function` and the lang items mentioned in its docs). /// Note that we deliberately show more details here than we do at runtime, such as the actual /// numbers that overflowed -- it is much easier to do so here than at runtime. pub fn fmt_assert_args(&self, f: &mut W) -> fmt::Result @@ -246,20 +247,44 @@ impl AssertKind { Overflow(BinOp::Shl, _, r) => { write!(f, "\"attempt to shift left by `{{}}`, which would overflow\", {r:?}") } + Overflow(op, _, _) => bug!("{:?} cannot overflow", op), MisalignedPointerDereference { required, found } => { write!( f, "\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\", {required:?}, {found:?}" ) } - _ => write!(f, "\"{}\"", self.description()), + ResumedAfterReturn(CoroutineKind::Coroutine(_)) => { + write!(f, "\"coroutine resumed after completion\"") + } + ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => { + write!(f, "\"`async fn` resumed after completion\"") + } + ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => { + write!(f, "\"`async gen fn` resumed after completion\"") + } + ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => { + write!(f, "\"`gen fn` should just keep returning `None` after completion\"") + } + ResumedAfterPanic(CoroutineKind::Coroutine(_)) => { + write!(f, "\"coroutine resumed after panicking\"") + } + ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => { + write!(f, "\"`async fn` resumed after panicking\"") + } + ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => { + write!(f, "\"`async gen fn` resumed after panicking\"") + } + ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => { + write!(f, "\"`gen fn` should just keep returning `None` after panicking\"") + } } } /// Format the diagnostic message for use in a lint (e.g. when the assertion fails during const-eval). /// /// Needs to be kept in sync with the run-time behavior (which is defined by - /// `AssertKind::description` and the lang items mentioned in its docs). + /// `AssertKind::panic_function` and the lang items mentioned in its docs). /// Note that we deliberately show more details here than we do at runtime, such as the actual /// numbers that overflowed -- it is much easier to do so here than at runtime. pub fn diagnostic_message(&self) -> DiagMessage { diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 3835bd371d996..4f7b2f7cbe48b 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -341,7 +341,7 @@ macro_rules! make_mir_visitor { ty::InstanceDef::Intrinsic(_def_id) | ty::InstanceDef::VTableShim(_def_id) | - ty::InstanceDef::ReifyShim(_def_id) | + ty::InstanceDef::ReifyShim(_def_id, _) | ty::InstanceDef::Virtual(_def_id, _) | ty::InstanceDef::ThreadLocalShim(_def_id) | ty::InstanceDef::ClosureOnceShim { call_once: _def_id, track_caller: _ } | diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 5e4454db3e28f..62a60a650eccf 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -877,12 +877,6 @@ rustc_queries! { desc { |tcx| "collecting all inherent impls for `{:?}`", key } } - /// The result of unsafety-checking this `LocalDefId` with the old checker. - query mir_unsafety_check_result(key: LocalDefId) -> &'tcx mir::UnsafetyCheckResult { - desc { |tcx| "unsafety-checking `{}`", tcx.def_path_str(key) } - cache_on_disk_if { true } - } - /// Unsafety-check this `LocalDefId`. query check_unsafety(key: LocalDefId) { desc { |tcx| "unsafety-checking `{}`", tcx.def_path_str(key) } diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index 9c7c46f2ad24b..8f02b3121acda 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -737,9 +737,10 @@ impl<'a, 'tcx> SpanDecoder for CacheDecoder<'a, 'tcx> { // If we get to this point, then all of the query inputs were green, // which means that the definition with this hash is guaranteed to // still exist in the current compilation session. - self.tcx.def_path_hash_to_def_id(def_path_hash, &mut || { - panic!("Failed to convert DefPathHash {def_path_hash:?}") - }) + self.tcx.def_path_hash_to_def_id( + def_path_hash, + &("Failed to convert DefPathHash", def_path_hash), + ) } fn decode_attr_id(&mut self) -> rustc_span::AttrId { diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index e3588a7afdc16..8cb4ee7bd41e0 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -339,7 +339,7 @@ macro_rules! define_callbacks { pub type Storage<'tcx> = <$($K)* as keys::Key>::Cache>; // Ensure that keys grow no larger than 64 bytes - #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] + #[cfg(all(any(target_arch = "x86_64", target_arch="aarch64"), target_pointer_width = "64"))] const _: () = { if mem::size_of::>() > 64 { panic!("{}", concat!( @@ -353,7 +353,7 @@ macro_rules! define_callbacks { }; // Ensure that values grow no larger than 64 bytes - #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] + #[cfg(all(any(target_arch = "x86_64", target_arch="aarch64"), target_pointer_width = "64"))] const _: () = { if mem::size_of::>() > 64 { panic!("{}", concat!( diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index b1162a34cdaf6..f10b204cd477b 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -12,12 +12,12 @@ use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_errors::{DiagArgValue, IntoDiagArg}; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_hir::RangeEnd; +use rustc_hir::{BindingAnnotation, ByRef, MatchSource, RangeEnd}; use rustc_index::newtype_index; use rustc_index::IndexVec; use rustc_middle::middle::region; use rustc_middle::mir::interpret::{AllocId, Scalar}; -use rustc_middle::mir::{self, BinOp, BorrowKind, FakeReadCause, Mutability, UnOp}; +use rustc_middle::mir::{self, BinOp, BorrowKind, FakeReadCause, UnOp}; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ @@ -358,6 +358,7 @@ pub enum ExprKind<'tcx> { scrutinee: ExprId, scrutinee_hir_id: hir::HirId, arms: Box<[ArmId]>, + match_source: MatchSource, }, /// A block. Block { @@ -581,12 +582,6 @@ pub enum InlineAsmOperand<'tcx> { }, } -#[derive(Copy, Clone, Debug, PartialEq, HashStable)] -pub enum BindingMode { - ByValue, - ByRef(BorrowKind), -} - #[derive(Clone, Debug, HashStable, TypeVisitable)] pub struct FieldPat<'tcx> { pub field: FieldIdx, @@ -607,19 +602,22 @@ impl<'tcx> Pat<'tcx> { pub fn simple_ident(&self) -> Option { match self.kind { - PatKind::Binding { name, mode: BindingMode::ByValue, subpattern: None, .. } => { - Some(name) - } + PatKind::Binding { + name, + mode: BindingAnnotation(ByRef::No, _), + subpattern: None, + .. + } => Some(name), _ => None, } } /// Call `f` on every "binding" in a pattern, e.g., on `a` in /// `match foo() { Some(a) => (), None => () }` - pub fn each_binding(&self, mut f: impl FnMut(Symbol, BindingMode, Ty<'tcx>, Span)) { + pub fn each_binding(&self, mut f: impl FnMut(Symbol, ByRef, Ty<'tcx>, Span)) { self.walk_always(|p| { if let PatKind::Binding { name, mode, ty, .. } = p.kind { - f(name, mode, ty, p.span); + f(name, mode.0, ty, p.span); } }); } @@ -730,10 +728,9 @@ pub enum PatKind<'tcx> { /// `x`, `ref x`, `x @ P`, etc. Binding { - mutability: Mutability, name: Symbol, #[type_visitable(ignore)] - mode: BindingMode, + mode: BindingAnnotation, #[type_visitable(ignore)] var: LocalVarId, ty: Ty<'tcx>, @@ -1073,17 +1070,8 @@ impl<'tcx> fmt::Display for Pat<'tcx> { PatKind::Wild => write!(f, "_"), PatKind::Never => write!(f, "!"), PatKind::AscribeUserType { ref subpattern, .. } => write!(f, "{subpattern}: _"), - PatKind::Binding { mutability, name, mode, ref subpattern, .. } => { - let is_mut = match mode { - BindingMode::ByValue => mutability == Mutability::Mut, - BindingMode::ByRef(bk) => { - write!(f, "ref ")?; - matches!(bk, BorrowKind::Mut { .. }) - } - }; - if is_mut { - write!(f, "mut ")?; - } + PatKind::Binding { name, mode, ref subpattern, .. } => { + f.write_str(mode.prefix_str())?; write!(f, "{name}")?; if let Some(ref subpattern) = *subpattern { write!(f, " @ {subpattern}")?; @@ -1133,7 +1121,8 @@ impl<'tcx> fmt::Display for Pat<'tcx> { printed += 1; } - if printed < variant.fields.len() { + let is_union = self.ty.ty_adt_def().is_some_and(|adt| adt.is_union()); + if printed < variant.fields.len() && (!is_union || printed == 0) { write!(f, "{}..", start_or_comma())?; } @@ -1217,7 +1206,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> { } // Some nodes are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] mod size_asserts { use super::*; // tidy-alphabetical-start diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index 99ab006bcc066..e42b85530b502 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -230,15 +230,7 @@ pub fn walk_pat<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( AscribeUserType { subpattern, ascription: _ } | Deref { subpattern } | DerefPattern { subpattern } - | Binding { - subpattern: Some(subpattern), - mutability: _, - mode: _, - var: _, - ty: _, - is_primary: _, - name: _, - } => visitor.visit_pat(subpattern), + | Binding { subpattern: Some(subpattern), .. } => visitor.visit_pat(subpattern), Binding { .. } | Wild | Never | Error(_) => {} Variant { subpatterns, adt_def: _, args: _, variant_index: _ } | Leaf { subpatterns } => { for subpattern in subpatterns { diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index efea2a66bb21f..ee81679191978 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -550,7 +550,7 @@ impl<'tcx> ObligationCauseCode<'tcx> { } // `ObligationCauseCode` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] static_assert_size!(ObligationCauseCode<'_>, 48); #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 8e9751f45294c..c35524373c7ac 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -193,7 +193,6 @@ pub enum SelectionCandidate<'tcx> { /// The evaluation results are ordered: /// - `EvaluatedToOk` implies `EvaluatedToOkModuloRegions` /// implies `EvaluatedToAmbig` implies `EvaluatedToAmbigStackDependent` -/// - `EvaluatedToErr` implies `EvaluatedToErrStackDependent` /// - the "union" of evaluation results is equal to their maximum - /// all the "potential success" candidates can potentially succeed, /// so they are noops when unioned with a definite error, and within @@ -219,52 +218,9 @@ pub enum EvaluationResult { /// variables. We are somewhat imprecise there, so we don't actually /// know the real result. /// - /// This can't be trivially cached for the same reason as `EvaluatedToErrStackDependent`. + /// This can't be trivially cached because the result depends on the + /// stack results. EvaluatedToAmbigStackDependent, - /// Evaluation failed because we encountered an obligation we are already - /// trying to prove on this branch. - /// - /// We know this branch can't be a part of a minimal proof-tree for - /// the "root" of our cycle, because then we could cut out the recursion - /// and maintain a valid proof tree. However, this does not mean - /// that all the obligations on this branch do not hold -- it's possible - /// that we entered this branch "speculatively", and that there - /// might be some other way to prove this obligation that does not - /// go through this cycle -- so we can't cache this as a failure. - /// - /// For example, suppose we have this: - /// - /// ```rust,ignore (pseudo-Rust) - /// pub trait Trait { fn xyz(); } - /// // This impl is "useless", but we can still have - /// // an `impl Trait for SomeUnsizedType` somewhere. - /// impl Trait for T { fn xyz() {} } - /// - /// pub fn foo() { - /// ::xyz(); - /// } - /// ``` - /// - /// When checking `foo`, we have to prove `T: Trait`. This basically - /// translates into this: - /// - /// ```plain,ignore - /// (T: Trait + Sized →_\impl T: Trait), T: Trait ⊢ T: Trait - /// ``` - /// - /// When we try to prove it, we first go the first option, which - /// recurses. This shows us that the impl is "useless" -- it won't - /// tell us that `T: Trait` unless it already implemented `Trait` - /// by some other means. However, that does not prevent `T: Trait` - /// does not hold, because of the bound (which can indeed be satisfied - /// by `SomeUnsizedType` from another crate). - // - // FIXME: when an `EvaluatedToErrStackDependent` goes past its parent root, we - // ought to convert it to an `EvaluatedToErr`, because we know - // there definitely isn't a proof tree for that obligation. Not - // doing so is still sound -- there isn't any proof tree, so the - // branch still can't be a part of a minimal one -- but does not re-enable caching. - EvaluatedToErrStackDependent, /// Evaluation failed. EvaluatedToErr, } @@ -290,13 +246,13 @@ impl EvaluationResult { | EvaluatedToAmbig | EvaluatedToAmbigStackDependent => true, - EvaluatedToErr | EvaluatedToErrStackDependent => false, + EvaluatedToErr => false, } } pub fn is_stack_dependent(self) -> bool { match self { - EvaluatedToAmbigStackDependent | EvaluatedToErrStackDependent => true, + EvaluatedToAmbigStackDependent => true, EvaluatedToOkModuloOpaqueTypes | EvaluatedToOk diff --git a/compiler/rustc_middle/src/traits/solve/inspect.rs b/compiler/rustc_middle/src/traits/solve/inspect.rs index 16842c8208f82..054772daab840 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect.rs @@ -121,8 +121,6 @@ pub enum ProbeStep<'tcx> { /// used whenever there are multiple candidates to prove the /// current goalby . NestedProbe(Probe<'tcx>), - CommitIfOkStart, - CommitIfOkSuccess, } /// What kind of probe we're in. In case the probe represents a candidate, or @@ -132,6 +130,8 @@ pub enum ProbeStep<'tcx> { pub enum ProbeKind<'tcx> { /// The root inference context while proving a goal. Root { result: QueryResult<'tcx> }, + /// Trying to normalize an alias by at least one stpe in `NormalizesTo`. + TryNormalizeNonRigid { result: QueryResult<'tcx> }, /// Probe entered when normalizing the self ty during candidate assembly NormalizedSelfTyAssembly, /// Some candidate to prove the current goal. @@ -143,9 +143,6 @@ pub enum ProbeKind<'tcx> { /// Used in the probe that wraps normalizing the non-self type for the unsize /// trait, which is also structurally matched on. UnsizeAssembly, - /// A call to `EvalCtxt::commit_if_ok` which failed, causing the work - /// to be discarded. - CommitIfOk, /// During upcasting from some source object to target object type, used to /// do a probe to find out what projection type(s) may be used to prove that /// the source type upholds all of the target type's object bounds. diff --git a/compiler/rustc_middle/src/traits/solve/inspect/format.rs b/compiler/rustc_middle/src/traits/solve/inspect/format.rs index 4393120501725..98f01fe877244 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect/format.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect/format.rs @@ -100,6 +100,9 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> { ProbeKind::Root { result } => { write!(self.f, "ROOT RESULT: {result:?}") } + ProbeKind::TryNormalizeNonRigid { result } => { + write!(self.f, "TRY NORMALIZE NON-RIGID: {result:?}") + } ProbeKind::NormalizedSelfTyAssembly => { write!(self.f, "NORMALIZING SELF TY FOR ASSEMBLY:") } @@ -109,9 +112,6 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> { ProbeKind::UpcastProjectionCompatibility => { write!(self.f, "PROBING FOR PROJECTION COMPATIBILITY FOR UPCASTING:") } - ProbeKind::CommitIfOk => { - write!(self.f, "COMMIT_IF_OK:") - } ProbeKind::MiscCandidate { name, result } => { write!(self.f, "CANDIDATE {name}: {result:?}") } @@ -132,8 +132,6 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> { } ProbeStep::EvaluateGoals(eval) => this.format_added_goals_evaluation(eval)?, ProbeStep::NestedProbe(probe) => this.format_probe(probe)?, - ProbeStep::CommitIfOkStart => writeln!(this.f, "COMMIT_IF_OK START")?, - ProbeStep::CommitIfOkSuccess => writeln!(this.f, "COMMIT_IF_OK SUCCESS")?, } } Ok(()) diff --git a/compiler/rustc_middle/src/ty/binding.rs b/compiler/rustc_middle/src/ty/binding.rs deleted file mode 100644 index af594bc5f24c8..0000000000000 --- a/compiler/rustc_middle/src/ty/binding.rs +++ /dev/null @@ -1,18 +0,0 @@ -use rustc_hir::{BindingAnnotation, ByRef, Mutability}; - -#[derive(Clone, PartialEq, TyEncodable, TyDecodable, Debug, Copy, HashStable)] -pub enum BindingMode { - BindByReference(Mutability), - BindByValue(Mutability), -} - -TrivialTypeTraversalImpls! { BindingMode } - -impl BindingMode { - pub fn convert(BindingAnnotation(by_ref, mutbl): BindingAnnotation) -> BindingMode { - match by_ref { - ByRef::No => BindingMode::BindByValue(mutbl), - ByRef::Yes => BindingMode::BindByReference(mutbl), - } - } -} diff --git a/compiler/rustc_middle/src/ty/cast.rs b/compiler/rustc_middle/src/ty/cast.rs index 50d629120ab5e..c9fd20bc1126a 100644 --- a/compiler/rustc_middle/src/ty/cast.rs +++ b/compiler/rustc_middle/src/ty/cast.rs @@ -83,9 +83,9 @@ pub fn mir_cast_kind<'tcx>(from_ty: Ty<'tcx>, cast_ty: Ty<'tcx>) -> mir::CastKin let cast = CastTy::from_ty(cast_ty); let cast_kind = match (from, cast) { (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Int(_))) => { - mir::CastKind::PointerExposeAddress + mir::CastKind::PointerExposeProvenance } - (Some(CastTy::Int(_)), Some(CastTy::Ptr(_))) => mir::CastKind::PointerFromExposedAddress, + (Some(CastTy::Int(_)), Some(CastTy::Ptr(_))) => mir::CastKind::PointerWithExposedProvenance, (_, Some(CastTy::DynStar)) => mir::CastKind::DynStar, (Some(CastTy::Int(_)), Some(CastTy::Int(_))) => mir::CastKind::IntToInt, (Some(CastTy::FnPtr), Some(CastTy::Ptr(_))) => mir::CastKind::FnPtrToPtr, diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index ddbc0bffaedde..e7a1679b151db 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -458,7 +458,6 @@ impl_decodable_via_ref! { &'tcx ty::List>, &'tcx traits::ImplSource<'tcx, ()>, &'tcx mir::Body<'tcx>, - &'tcx mir::UnsafetyCheckResult, &'tcx mir::BorrowCheckResult<'tcx>, &'tcx mir::coverage::CodeRegion, &'tcx ty::List, diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 3713883eb00d7..49b806b8369f8 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -59,7 +59,7 @@ pub struct ConstData<'tcx> { pub kind: ConstKind<'tcx>, } -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] static_assert_size!(ConstData<'_>, 40); impl<'tcx> Const<'tcx> { diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs index ea02faca5f398..94e41709f5da6 100644 --- a/compiler/rustc_middle/src/ty/consts/kind.rs +++ b/compiler/rustc_middle/src/ty/consts/kind.rs @@ -71,8 +71,8 @@ pub enum Expr<'tcx> { Cast(CastKind, Const<'tcx>, Ty<'tcx>), } -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] static_assert_size!(Expr<'_>, 24); -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] static_assert_size!(super::ConstKind<'_>, 32); diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 188cb50849dc1..75deffe695764 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -956,6 +956,13 @@ impl<'tcx> TyCtxt<'tcx> { self.get_lang_items(()) } + /// Gets a `Ty` representing the [`LangItem::OrderingEnum`] + #[track_caller] + pub fn ty_ordering_enum(self, span: Option) -> Ty<'tcx> { + let ordering_enum = self.require_lang_item(hir::LangItem::OrderingEnum, span); + self.type_of(ordering_enum).no_bound_vars().unwrap() + } + /// Obtain the given diagnostic item's `DefId`. Use `is_diagnostic_item` if you just want to /// compare against another `DefId`, since `is_diagnostic_item` is cheaper. pub fn get_diagnostic_item(self, name: Symbol) -> Option { @@ -1114,7 +1121,11 @@ impl<'tcx> TyCtxt<'tcx> { /// Converts a `DefPathHash` to its corresponding `DefId` in the current compilation /// session, if it still exists. This is used during incremental compilation to /// turn a deserialized `DefPathHash` into its current `DefId`. - pub fn def_path_hash_to_def_id(self, hash: DefPathHash, err: &mut dyn FnMut() -> !) -> DefId { + pub fn def_path_hash_to_def_id( + self, + hash: DefPathHash, + err_msg: &dyn std::fmt::Debug, + ) -> DefId { debug!("def_path_hash_to_def_id({:?})", hash); let stable_crate_id = hash.stable_crate_id(); @@ -1122,7 +1133,11 @@ impl<'tcx> TyCtxt<'tcx> { // If this is a DefPathHash from the local crate, we can look up the // DefId in the tcx's `Definitions`. if stable_crate_id == self.stable_crate_id(LOCAL_CRATE) { - self.untracked.definitions.read().local_def_path_hash_to_def_id(hash, err).to_def_id() + self.untracked + .definitions + .read() + .local_def_path_hash_to_def_id(hash, err_msg) + .to_def_id() } else { // If this is a DefPathHash from an upstream crate, let the CrateStore map // it to a DefId. @@ -1954,33 +1969,104 @@ impl<'tcx> TyCtxt<'tcx> { if pred.kind() != binder { self.mk_predicate(binder) } else { pred } } - #[inline(always)] - pub(crate) fn check_and_mk_args( + pub fn check_args_compatible(self, def_id: DefId, args: &'tcx [ty::GenericArg<'tcx>]) -> bool { + self.check_args_compatible_inner(def_id, args, false) + } + + fn check_args_compatible_inner( self, - _def_id: DefId, - args: impl IntoIterator>>, - ) -> GenericArgsRef<'tcx> { - let args = args.into_iter().map(Into::into); - #[cfg(debug_assertions)] + def_id: DefId, + args: &'tcx [ty::GenericArg<'tcx>], + nested: bool, + ) -> bool { + let generics = self.generics_of(def_id); + + // IATs themselves have a weird arg setup (self + own args), but nested items *in* IATs + // (namely: opaques, i.e. ATPITs) do not. + let own_args = if !nested + && let DefKind::AssocTy = self.def_kind(def_id) + && let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(def_id)) { - let generics = self.generics_of(_def_id); + if generics.params.len() + 1 != args.len() { + return false; + } + + if !matches!(args[0].unpack(), ty::GenericArgKind::Type(_)) { + return false; + } + + &args[1..] + } else { + if generics.count() != args.len() { + return false; + } - let n = if let DefKind::AssocTy = self.def_kind(_def_id) - && let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(_def_id)) + let (parent_args, own_args) = args.split_at(generics.parent_count); + + if let Some(parent) = generics.parent + && !self.check_args_compatible_inner(parent, parent_args, true) { - // If this is an inherent projection. - generics.params.len() + 1 - } else { - generics.count() - }; - assert_eq!( - (n, Some(n)), - args.size_hint(), - "wrong number of generic parameters for {_def_id:?}: {:?}", - args.collect::>(), - ); + return false; + } + + own_args + }; + + for (param, arg) in std::iter::zip(&generics.params, own_args) { + match (¶m.kind, arg.unpack()) { + (ty::GenericParamDefKind::Type { .. }, ty::GenericArgKind::Type(_)) + | (ty::GenericParamDefKind::Lifetime, ty::GenericArgKind::Lifetime(_)) + | (ty::GenericParamDefKind::Const { .. }, ty::GenericArgKind::Const(_)) => {} + _ => return false, + } } - self.mk_args_from_iter(args) + + true + } + + /// With `cfg(debug_assertions)`, assert that args are compatible with their generics, + /// and print out the args if not. + pub fn debug_assert_args_compatible(self, def_id: DefId, args: &'tcx [ty::GenericArg<'tcx>]) { + if cfg!(debug_assertions) { + if !self.check_args_compatible(def_id, args) { + if let DefKind::AssocTy = self.def_kind(def_id) + && let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(def_id)) + { + bug!( + "args not compatible with generics for {}: args={:#?}, generics={:#?}", + self.def_path_str(def_id), + args, + // Make `[Self, GAT_ARGS...]` (this could be simplified) + self.mk_args_from_iter( + [self.types.self_param.into()].into_iter().chain( + self.generics_of(def_id) + .own_args(ty::GenericArgs::identity_for_item(self, def_id)) + .iter() + .copied() + ) + ) + ); + } else { + bug!( + "args not compatible with generics for {}: args={:#?}, generics={:#?}", + self.def_path_str(def_id), + args, + ty::GenericArgs::identity_for_item(self, def_id) + ); + } + } + } + } + + #[inline(always)] + pub(crate) fn check_and_mk_args( + self, + def_id: DefId, + args: impl IntoIterator>>, + ) -> GenericArgsRef<'tcx> { + let args = self.mk_args_from_iter(args.into_iter().map(Into::into)); + self.debug_assert_args_compatible(def_id, args); + args } #[inline] diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 4fec5653a798c..4a7720b38f850 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -31,6 +31,28 @@ pub struct Instance<'tcx> { pub args: GenericArgsRef<'tcx>, } +/// Describes why a `ReifyShim` was created. This is needed to distingish a ReifyShim created to +/// adjust for things like `#[track_caller]` in a vtable from a `ReifyShim` created to produce a +/// function pointer from a vtable entry. +/// Currently, this is only used when KCFI is enabled, as only KCFI needs to treat those two +/// `ReifyShim`s differently. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(TyEncodable, TyDecodable, HashStable)] +pub enum ReifyReason { + /// The `ReifyShim` was created to produce a function pointer. This happens when: + /// * A vtable entry is directly converted to a function call (e.g. creating a fn ptr from a + /// method on a `dyn` object). + /// * A function with `#[track_caller]` is converted to a function pointer + /// * If KCFI is enabled, creating a function pointer from a method on an object-safe trait. + /// This includes the case of converting `::call`-like methods on closure-likes to function + /// pointers. + FnPtr, + /// This `ReifyShim` was created to populate a vtable. Currently, this happens when a + /// `#[track_caller]` mismatch occurs between the implementation of a method and the method. + /// This includes the case of `::call`-like methods in closure-likes' vtables. + Vtable, +} + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[derive(TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable, Lift)] pub enum InstanceDef<'tcx> { @@ -67,7 +89,13 @@ pub enum InstanceDef<'tcx> { /// Because this is a required part of the function's ABI but can't be tracked /// as a property of the function pointer, we use a single "caller location" /// (the definition of the function itself). - ReifyShim(DefId), + /// + /// The second field encodes *why* this shim was created. This allows distinguishing between + /// a `ReifyShim` that appears in a vtable vs one that appears as a function pointer. + /// + /// This field will only be populated if we are compiling in a mode that needs these shims + /// to be separable, currently only when KCFI is enabled. + ReifyShim(DefId, Option), /// `::call_*` (generated `FnTrait` implementation for `fn()` pointers). /// @@ -194,7 +222,7 @@ impl<'tcx> InstanceDef<'tcx> { match self { InstanceDef::Item(def_id) | InstanceDef::VTableShim(def_id) - | InstanceDef::ReifyShim(def_id) + | InstanceDef::ReifyShim(def_id, _) | InstanceDef::FnPtrShim(def_id, _) | InstanceDef::Virtual(def_id, _) | InstanceDef::Intrinsic(def_id) @@ -354,7 +382,9 @@ fn fmt_instance( match instance.def { InstanceDef::Item(_) => Ok(()), InstanceDef::VTableShim(_) => write!(f, " - shim(vtable)"), - InstanceDef::ReifyShim(_) => write!(f, " - shim(reify)"), + InstanceDef::ReifyShim(_, None) => write!(f, " - shim(reify)"), + InstanceDef::ReifyShim(_, Some(ReifyReason::FnPtr)) => write!(f, " - shim(reify-fnptr)"), + InstanceDef::ReifyShim(_, Some(ReifyReason::Vtable)) => write!(f, " - shim(reify-vtable)"), InstanceDef::ThreadLocalShim(_) => write!(f, " - shim(tls)"), InstanceDef::Intrinsic(_) => write!(f, " - intrinsic"), InstanceDef::Virtual(_, num) => write!(f, " - virtual#{num}"), @@ -476,15 +506,34 @@ impl<'tcx> Instance<'tcx> { debug!("resolve(def_id={:?}, args={:?})", def_id, args); // Use either `resolve_closure` or `resolve_for_vtable` assert!(!tcx.is_closure_like(def_id), "Called `resolve_for_fn_ptr` on closure: {def_id:?}"); + let reason = tcx.sess.is_sanitizer_kcfi_enabled().then_some(ReifyReason::FnPtr); Instance::resolve(tcx, param_env, def_id, args).ok().flatten().map(|mut resolved| { match resolved.def { InstanceDef::Item(def) if resolved.def.requires_caller_location(tcx) => { debug!(" => fn pointer created for function with #[track_caller]"); - resolved.def = InstanceDef::ReifyShim(def); + resolved.def = InstanceDef::ReifyShim(def, reason); } InstanceDef::Virtual(def_id, _) => { debug!(" => fn pointer created for virtual call"); - resolved.def = InstanceDef::ReifyShim(def_id); + resolved.def = InstanceDef::ReifyShim(def_id, reason); + } + // Reify `Trait::method` implementations if KCFI is enabled + // FIXME(maurer) only reify it if it is a vtable-safe function + _ if tcx.sess.is_sanitizer_kcfi_enabled() + && tcx.associated_item(def_id).trait_item_def_id.is_some() => + { + // If this function could also go in a vtable, we need to `ReifyShim` it with + // KCFI because it can only attach one type per function. + resolved.def = InstanceDef::ReifyShim(resolved.def_id(), reason) + } + // Reify `::call`-like method implementations if KCFI is enabled + _ if tcx.sess.is_sanitizer_kcfi_enabled() + && tcx.is_closure_like(resolved.def_id()) => + { + // Reroute through a reify via the *unresolved* instance. The resolved one can't + // be directly reified because it's closure-like. The reify can handle the + // unresolved instance. + resolved = Instance { def: InstanceDef::ReifyShim(def_id, reason), args } } _ => {} } @@ -508,6 +557,7 @@ impl<'tcx> Instance<'tcx> { debug!(" => associated item with unsizeable self: Self"); Some(Instance { def: InstanceDef::VTableShim(def_id), args }) } else { + let reason = tcx.sess.is_sanitizer_kcfi_enabled().then_some(ReifyReason::Vtable); Instance::resolve(tcx, param_env, def_id, args).ok().flatten().map(|mut resolved| { match resolved.def { InstanceDef::Item(def) => { @@ -544,18 +594,18 @@ impl<'tcx> Instance<'tcx> { // Create a shim for the `FnOnce/FnMut/Fn` method we are calling // - unlike functions, invoking a closure always goes through a // trait. - resolved = Instance { def: InstanceDef::ReifyShim(def_id), args }; + resolved = Instance { def: InstanceDef::ReifyShim(def_id, reason), args }; } else { debug!( " => vtable fn pointer created for function with #[track_caller]: {:?}", def ); - resolved.def = InstanceDef::ReifyShim(def); + resolved.def = InstanceDef::ReifyShim(def, reason); } } } InstanceDef::Virtual(def_id, _) => { debug!(" => vtable fn pointer created for virtual call"); - resolved.def = InstanceDef::ReifyShim(def_id); + resolved.def = InstanceDef::ReifyShim(def_id, reason) } _ => {} } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 66fee515ab2ce..fb56c71c517b4 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -76,8 +76,6 @@ pub use rustc_type_ir::ConstKind::{ }; pub use rustc_type_ir::*; -pub use self::binding::BindingMode; -pub use self::binding::BindingMode::*; pub use self::closure::{ is_ancestor_or_same_capture, place_to_string_for_capture, BorrowKind, CaptureInfo, CapturedPlace, ClosureTypeInfo, MinCaptureInformationMap, MinCaptureList, @@ -90,7 +88,7 @@ pub use self::context::{ tls, CtxtInterners, CurrentGcx, DeducedParamAttrs, Feed, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt, TyCtxtFeed, }; -pub use self::instance::{Instance, InstanceDef, ShortInstance, UnusedGenericParams}; +pub use self::instance::{Instance, InstanceDef, ReifyReason, ShortInstance, UnusedGenericParams}; pub use self::list::List; pub use self::parameterized::ParameterizedOverTcx; pub use self::predicate::{ @@ -123,7 +121,6 @@ pub use self::typeck_results::{ pub mod _match; pub mod abstract_const; pub mod adjustment; -pub mod binding; pub mod cast; pub mod codec; pub mod error; @@ -1037,9 +1034,11 @@ impl PlaceholderLike for PlaceholderConst { } } -/// When type checking, we use the `ParamEnv` to track -/// details about the set of where-clauses that are in scope at this -/// particular point. +/// When interacting with the type system we must provide information about the +/// environment. `ParamEnv` is the type that represents this information. See the +/// [dev guide chapter][param_env_guide] for more information. +/// +/// [param_env_guide]: https://rustc-dev-guide.rust-lang.org/param_env/param_env_summary.html #[derive(Copy, Clone, Hash, PartialEq, Eq)] pub struct ParamEnv<'tcx> { /// This packs both caller bounds and the reveal enum into one pointer. @@ -1106,8 +1105,11 @@ impl<'tcx> TypeVisitable> for ParamEnv<'tcx> { impl<'tcx> ParamEnv<'tcx> { /// Construct a trait environment suitable for contexts where /// there are no where-clauses in scope. Hidden types (like `impl - /// Trait`) are left hidden, so this is suitable for ordinary - /// type-checking. + /// Trait`) are left hidden. In majority of cases it is incorrect + /// to use an empty environment. See the [dev guide section][param_env_guide] + /// for information on what a `ParamEnv` is and how to acquire one. + /// + /// [param_env_guide]: https://rustc-dev-guide.rust-lang.org/param_env/param_env_summary.html #[inline] pub fn empty() -> Self { Self::new(List::empty(), Reveal::UserFacing) @@ -1322,7 +1324,7 @@ impl VariantDef { pub fn single_field(&self) -> &FieldDef { assert!(self.fields.len() == 1); - &self.fields[FieldIdx::from_u32(0)] + &self.fields[FieldIdx::ZERO] } /// Returns the last field in this variant, if present. @@ -1555,7 +1557,6 @@ impl<'tcx> TyCtxt<'tcx> { attr::ReprRust => ReprFlags::empty(), attr::ReprC => ReprFlags::IS_C, attr::ReprPacked(pack) => { - let pack = Align::from_bytes(pack as u64).unwrap(); min_pack = Some(if let Some(min_pack) = min_pack { min_pack.min(pack) } else { @@ -1587,7 +1588,7 @@ impl<'tcx> TyCtxt<'tcx> { ReprFlags::empty() } attr::ReprAlign(align) => { - max_align = max_align.max(Some(Align::from_bytes(align as u64).unwrap())); + max_align = max_align.max(Some(align)); ReprFlags::empty() } }); @@ -2167,7 +2168,7 @@ pub struct DestructuredConst<'tcx> { } // Some types are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; diff --git a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs index d4c8f5900f9ee..5854367446022 100644 --- a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs +++ b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs @@ -49,7 +49,7 @@ impl<'tcx> TyCtxt<'tcx> { let value = self.erase_regions(value); debug!(?value); - if !value.has_projections() { + if !value.has_aliases() { value } else { value.fold_with(&mut NormalizeAfterErasingRegionsFolder { tcx: self, param_env }) @@ -81,7 +81,7 @@ impl<'tcx> TyCtxt<'tcx> { let value = self.erase_regions(value); debug!(?value); - if !value.has_projections() { + if !value.has_aliases() { Ok(value) } else { let mut folder = TryNormalizeAfterErasingRegionsFolder::new(self, param_env); diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 5ff98dc8c873f..2a898430ce9f9 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2589,7 +2589,7 @@ impl<'a, 'tcx> ty::TypeFolder> for RegionFolder<'a, 'tcx> { ty::BrAnon | ty::BrEnv => r, _ => { // Index doesn't matter, since this is just for naming and these never get bound - let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind }; + let br = ty::BoundRegion { var: ty::BoundVar::ZERO, kind }; *self .region_map .entry(br) diff --git a/compiler/rustc_middle/src/ty/region.rs b/compiler/rustc_middle/src/ty/region.rs index 867faf6326199..b92800a172880 100644 --- a/compiler/rustc_middle/src/ty/region.rs +++ b/compiler/rustc_middle/src/ty/region.rs @@ -140,6 +140,10 @@ impl<'tcx> rustc_type_ir::new::Region> for Region<'tcx> { fn new_anon_bound(tcx: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self { Region::new_bound(tcx, debruijn, ty::BoundRegion { var, kind: ty::BoundRegionKind::BrAnon }) } + + fn new_static(tcx: TyCtxt<'tcx>) -> Self { + tcx.lifetimes.re_static + } } /// Region utilities diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index f14ca7ae4b7a9..0e7010e67d7dc 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -402,6 +402,7 @@ TrivialTypeTraversalImpls! { ::rustc_span::symbol::Symbol, ::rustc_hir::def::Res, ::rustc_hir::def_id::LocalDefId, + ::rustc_hir::ByRef, ::rustc_hir::HirId, ::rustc_hir::MatchSource, ::rustc_target::asm::InlineAsmRegOrRegClass, @@ -448,6 +449,7 @@ TrivialTypeTraversalAndLiftImpls! { crate::ty::ClosureKind, crate::ty::ParamConst, crate::ty::ParamTy, + crate::ty::instance::ReifyReason, interpret::AllocId, interpret::CtfeProvenance, interpret::Scalar, diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 57a675e4453b8..2ab63f01e7c17 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -11,7 +11,7 @@ use crate::ty::{ }; use crate::ty::{GenericArg, GenericArgs, GenericArgsRef}; use crate::ty::{List, ParamEnv}; -use hir::def::DefKind; +use hir::def::{CtorKind, DefKind}; use rustc_data_structures::captures::Captures; use rustc_errors::{DiagArgValue, ErrorGuaranteed, IntoDiagArg, MultiSpan}; use rustc_hir as hir; @@ -1624,6 +1624,7 @@ impl<'tcx> Ty<'tcx> { #[inline] pub fn new_adt(tcx: TyCtxt<'tcx>, def: AdtDef<'tcx>, args: GenericArgsRef<'tcx>) -> Ty<'tcx> { + tcx.debug_assert_args_compatible(def.did(), args); Ty::new(tcx, Adt(def, args)) } @@ -1670,6 +1671,10 @@ impl<'tcx> Ty<'tcx> { def_id: DefId, args: impl IntoIterator>>, ) -> Ty<'tcx> { + debug_assert_matches!( + tcx.def_kind(def_id), + DefKind::AssocFn | DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) + ); let args = tcx.check_and_mk_args(def_id, args); Ty::new(tcx, FnDef(def_id, args)) } @@ -1704,11 +1709,7 @@ impl<'tcx> Ty<'tcx> { def_id: DefId, closure_args: GenericArgsRef<'tcx>, ) -> Ty<'tcx> { - debug_assert_eq!( - closure_args.len(), - tcx.generics_of(tcx.typeck_root_def_id(def_id)).count() + 3, - "closure constructed with incorrect generic parameters" - ); + tcx.debug_assert_args_compatible(def_id, closure_args); Ty::new(tcx, Closure(def_id, closure_args)) } @@ -1718,11 +1719,7 @@ impl<'tcx> Ty<'tcx> { def_id: DefId, closure_args: GenericArgsRef<'tcx>, ) -> Ty<'tcx> { - debug_assert_eq!( - closure_args.len(), - tcx.generics_of(tcx.typeck_root_def_id(def_id)).count() + 5, - "closure constructed with incorrect generic parameters" - ); + tcx.debug_assert_args_compatible(def_id, closure_args); Ty::new(tcx, CoroutineClosure(def_id, closure_args)) } @@ -1732,11 +1729,7 @@ impl<'tcx> Ty<'tcx> { def_id: DefId, coroutine_args: GenericArgsRef<'tcx>, ) -> Ty<'tcx> { - debug_assert_eq!( - coroutine_args.len(), - tcx.generics_of(tcx.typeck_root_def_id(def_id)).count() + 6, - "coroutine constructed with incorrect number of generic parameters" - ); + tcx.debug_assert_args_compatible(def_id, coroutine_args); Ty::new(tcx, Coroutine(def_id, coroutine_args)) } @@ -1947,7 +1940,7 @@ impl<'tcx> Ty<'tcx> { Adt(def, args) => { assert!(def.repr().simd(), "`simd_size_and_type` called on non-SIMD type"); let variant = def.non_enum_variant(); - let f0_ty = variant.fields[FieldIdx::from_u32(0)].ty(tcx, args); + let f0_ty = variant.fields[FieldIdx::ZERO].ty(tcx, args); match f0_ty.kind() { // If the first field is an array, we assume it is the only field and its @@ -2692,7 +2685,7 @@ impl<'tcx> VarianceDiagInfo<'tcx> { } // Some types are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 827b7e088ce0f..995feeaa1487f 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -3,8 +3,8 @@ use crate::{ infer::canonical::Canonical, traits::ObligationCause, ty::{ - self, tls, BindingMode, BoundVar, CanonicalPolyFnSig, ClosureSizeProfileData, - GenericArgKind, GenericArgs, GenericArgsRef, Ty, UserArgs, + self, tls, BoundVar, CanonicalPolyFnSig, ClosureSizeProfileData, GenericArgKind, + GenericArgs, GenericArgsRef, Ty, UserArgs, }, }; use rustc_data_structures::{ @@ -12,14 +12,14 @@ use rustc_data_structures::{ unord::{ExtendUnord, UnordItems, UnordSet}, }; use rustc_errors::ErrorGuaranteed; -use rustc_hir as hir; use rustc_hir::{ + self as hir, def::{DefKind, Res}, def_id::{DefId, LocalDefId, LocalDefIdMap}, hir_id::OwnerId, - HirId, ItemLocalId, ItemLocalMap, ItemLocalSet, + BindingAnnotation, ByRef, HirId, ItemLocalId, ItemLocalMap, ItemLocalSet, Mutability, }; -use rustc_index::{Idx, IndexVec}; +use rustc_index::IndexVec; use rustc_macros::HashStable; use rustc_middle::mir::FakeReadCause; use rustc_session::Session; @@ -78,8 +78,8 @@ pub struct TypeckResults<'tcx> { adjustments: ItemLocalMap>>, - /// Stores the actual binding mode for all instances of hir::BindingAnnotation. - pat_binding_modes: ItemLocalMap, + /// Stores the actual binding mode for all instances of [`BindingAnnotation`]. + pat_binding_modes: ItemLocalMap, /// Stores the types which were implicitly dereferenced in pattern binding modes /// for later usage in THIR lowering. For example, @@ -96,6 +96,10 @@ pub struct TypeckResults<'tcx> { /// pat_adjustments: ItemLocalMap>>, + /// Set of reference patterns that match against a match-ergonomics inserted reference + /// (as opposed to against a reference in the scrutinee type). + skipped_ref_pats: ItemLocalSet, + /// Records the reasons that we picked the kind of each closure; /// not all closures are present in the map. closure_kind_origins: ItemLocalMap<(Span, HirPlace<'tcx>)>, @@ -228,6 +232,7 @@ impl<'tcx> TypeckResults<'tcx> { adjustments: Default::default(), pat_binding_modes: Default::default(), pat_adjustments: Default::default(), + skipped_ref_pats: Default::default(), closure_kind_origins: Default::default(), liberated_fn_sigs: Default::default(), fru_field_types: Default::default(), @@ -408,17 +413,22 @@ impl<'tcx> TypeckResults<'tcx> { matches!(self.type_dependent_defs().get(expr.hir_id), Some(Ok((DefKind::AssocFn, _)))) } - pub fn extract_binding_mode(&self, s: &Session, id: HirId, sp: Span) -> Option { + pub fn extract_binding_mode( + &self, + s: &Session, + id: HirId, + sp: Span, + ) -> Option { self.pat_binding_modes().get(id).copied().or_else(|| { s.dcx().span_bug(sp, "missing binding mode"); }) } - pub fn pat_binding_modes(&self) -> LocalTableInContext<'_, BindingMode> { + pub fn pat_binding_modes(&self) -> LocalTableInContext<'_, BindingAnnotation> { LocalTableInContext { hir_owner: self.hir_owner, data: &self.pat_binding_modes } } - pub fn pat_binding_modes_mut(&mut self) -> LocalTableInContextMut<'_, BindingMode> { + pub fn pat_binding_modes_mut(&mut self) -> LocalTableInContextMut<'_, BindingAnnotation> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_binding_modes } } @@ -430,6 +440,14 @@ impl<'tcx> TypeckResults<'tcx> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_adjustments } } + pub fn skipped_ref_pats(&self) -> LocalSetInContext<'_> { + LocalSetInContext { hir_owner: self.hir_owner, data: &self.skipped_ref_pats } + } + + pub fn skipped_ref_pats_mut(&mut self) -> LocalSetInContextMut<'_> { + LocalSetInContextMut { hir_owner: self.hir_owner, data: &mut self.skipped_ref_pats } + } + /// Does the pattern recursively contain a `ref mut` binding in it? /// /// This is used to determined whether a `deref` pattern should emit a `Deref` @@ -442,7 +460,7 @@ impl<'tcx> TypeckResults<'tcx> { let mut has_ref_mut = false; pat.walk(|pat| { if let hir::PatKind::Binding(_, id, _, _) = pat.kind - && let Some(ty::BindByReference(ty::Mutability::Mut)) = + && let Some(BindingAnnotation(ByRef::Yes(Mutability::Mut), _)) = self.pat_binding_modes().get(id) { has_ref_mut = true; @@ -624,6 +642,49 @@ impl<'a, V> LocalTableInContextMut<'a, V> { } } +#[derive(Clone, Copy, Debug)] +pub struct LocalSetInContext<'a> { + hir_owner: OwnerId, + data: &'a ItemLocalSet, +} + +impl<'a> LocalSetInContext<'a> { + pub fn is_empty(&self) -> bool { + self.data.is_empty() + } + + pub fn contains(&self, id: hir::HirId) -> bool { + validate_hir_id_for_typeck_results(self.hir_owner, id); + self.data.contains(&id.local_id) + } +} + +#[derive(Debug)] +pub struct LocalSetInContextMut<'a> { + hir_owner: OwnerId, + data: &'a mut ItemLocalSet, +} + +impl<'a> LocalSetInContextMut<'a> { + pub fn is_empty(&self) -> bool { + self.data.is_empty() + } + + pub fn contains(&self, id: hir::HirId) -> bool { + validate_hir_id_for_typeck_results(self.hir_owner, id); + self.data.contains(&id.local_id) + } + pub fn insert(&mut self, id: hir::HirId) -> bool { + validate_hir_id_for_typeck_results(self.hir_owner, id); + self.data.insert(id.local_id) + } + + pub fn remove(&mut self, id: hir::HirId) -> bool { + validate_hir_id_for_typeck_results(self.hir_owner, id); + self.data.remove(&id.local_id) + } +} + rustc_index::newtype_index! { #[derive(HashStable)] #[encodable] @@ -675,7 +736,7 @@ impl<'tcx> IsIdentity for CanonicalUserType<'tcx> { return false; } - iter::zip(user_args.args, BoundVar::new(0)..).all(|(kind, cvar)| { + iter::zip(user_args.args, BoundVar::ZERO..).all(|(kind, cvar)| { match kind.unpack() { GenericArgKind::Type(ty) => match ty.kind() { ty::Bound(debruijn, b) => { diff --git a/compiler/rustc_mir_build/src/build/block.rs b/compiler/rustc_mir_build/src/build/block.rs index 4046122b6fe67..00e99f330f727 100644 --- a/compiler/rustc_mir_build/src/build/block.rs +++ b/compiler/rustc_mir_build/src/build/block.rs @@ -13,31 +13,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ast_block: BlockId, source_info: SourceInfo, ) -> BlockAnd<()> { - let Block { region_scope, span, ref stmts, expr, targeted_by_break, safety_mode } = + let Block { region_scope, span, ref stmts, expr, targeted_by_break, safety_mode: _ } = self.thir[ast_block]; self.in_scope((region_scope, source_info), LintLevel::Inherited, move |this| { if targeted_by_break { this.in_breakable_scope(None, destination, span, |this| { - Some(this.ast_block_stmts( - destination, - block, - span, - stmts, - expr, - safety_mode, - region_scope, - )) + Some(this.ast_block_stmts(destination, block, span, stmts, expr, region_scope)) }) } else { - this.ast_block_stmts( - destination, - block, - span, - stmts, - expr, - safety_mode, - region_scope, - ) + this.ast_block_stmts(destination, block, span, stmts, expr, region_scope) } }) } @@ -49,7 +33,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { span: Span, stmts: &[StmtId], expr: Option, - safety_mode: BlockSafety, region_scope: Scope, ) -> BlockAnd<()> { let this = self; @@ -72,13 +55,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // First we build all the statements in the block. let mut let_scope_stack = Vec::with_capacity(8); let outer_source_scope = this.source_scope; - let outer_in_scope_unsafe = this.in_scope_unsafe; // This scope information is kept for breaking out of the parent remainder scope in case // one let-else pattern matching fails. // By doing so, we can be sure that even temporaries that receive extended lifetime // assignments are dropped, too. let mut last_remainder_scope = region_scope; - this.update_source_scope_for_safety_mode(span, safety_mode); let source_info = this.source_info(span); for stmt in stmts { @@ -202,7 +183,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let_scope_stack.push(remainder_scope); let visibility_scope = - Some(this.new_source_scope(remainder_span, LintLevel::Inherited, None)); + Some(this.new_source_scope(remainder_span, LintLevel::Inherited)); let initializer_span = this.thir[*initializer].span; let scope = (*init_scope, source_info); @@ -218,7 +199,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.visit_primary_bindings( pattern, UserTypeProjections::none(), - &mut |this, _, _, _, node, span, _, _| { + &mut |this, _, _, node, span, _, _| { this.storage_live_binding( block, node, @@ -271,7 +252,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let remainder_span = remainder_scope.span(this.tcx, this.region_scope_tree); let visibility_scope = - Some(this.new_source_scope(remainder_span, LintLevel::Inherited, None)); + Some(this.new_source_scope(remainder_span, LintLevel::Inherited)); // Evaluate the initializer, if present. if let Some(init) = *initializer { @@ -308,7 +289,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.visit_primary_bindings( pattern, UserTypeProjections::none(), - &mut |this, _, _, _, node, span, _, _| { + &mut |this, _, _, node, span, _, _| { this.storage_live_binding(block, node, span, OutsideGuard, true); this.schedule_drop_for_binding(node, span, OutsideGuard); }, @@ -364,22 +345,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } // Restore the original source scope. this.source_scope = outer_source_scope; - this.in_scope_unsafe = outer_in_scope_unsafe; block.unit() } - - /// If we are entering an unsafe block, create a new source scope - fn update_source_scope_for_safety_mode(&mut self, span: Span, safety_mode: BlockSafety) { - debug!("update_source_scope_for({:?}, {:?})", span, safety_mode); - let new_unsafety = match safety_mode { - BlockSafety::Safe => return, - BlockSafety::BuiltinUnsafe => Safety::BuiltinUnsafe, - BlockSafety::ExplicitUnsafe(hir_id) => { - self.in_scope_unsafe = Safety::ExplicitUnsafe(hir_id); - Safety::ExplicitUnsafe(hir_id) - } - }; - - self.source_scope = self.new_source_scope(span, LintLevel::Inherited, Some(new_unsafety)); - } } diff --git a/compiler/rustc_mir_build/src/build/custom/mod.rs b/compiler/rustc_mir_build/src/build/custom/mod.rs index 0475bb8908b29..30877e38318d6 100644 --- a/compiler/rustc_mir_build/src/build/custom/mod.rs +++ b/compiler/rustc_mir_build/src/build/custom/mod.rs @@ -72,10 +72,7 @@ pub(super) fn build_custom_mir<'tcx>( parent_scope: None, inlined: None, inlined_parent_scope: None, - local_data: ClearCrossCrate::Set(SourceScopeLocalData { - lint_root: hir_id, - safety: Safety::Safe, - }), + local_data: ClearCrossCrate::Set(SourceScopeLocalData { lint_root: hir_id }), }); body.injection_phase = Some(parse_attribute(attr)); diff --git a/compiler/rustc_mir_build/src/build/custom/parse.rs b/compiler/rustc_mir_build/src/build/custom/parse.rs index a6f9caada2d19..0384b9bc154e2 100644 --- a/compiler/rustc_mir_build/src/build/custom/parse.rs +++ b/compiler/rustc_mir_build/src/build/custom/parse.rs @@ -215,7 +215,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { fn parse_local_decls(&mut self, mut stmts: impl Iterator) -> PResult<()> { let (ret_var, ..) = self.parse_let_statement(stmts.next().unwrap())?; - self.local_map.insert(ret_var, Local::from_u32(0)); + self.local_map.insert(ret_var, Local::ZERO); for stmt in stmts { let (var, ty, span) = self.parse_let_statement(stmt)?; diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs index c77f4a06d0569..260ab058e600c 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -118,19 +118,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ExprKind::Box { value } => { let value_ty = this.thir[value].ty; let tcx = this.tcx; - - // `exchange_malloc` is unsafe but box is safe, so need a new scope. - let synth_scope = this.new_source_scope( - expr_span, - LintLevel::Inherited, - Some(Safety::BuiltinUnsafe), - ); - let synth_info = SourceInfo { span: expr_span, scope: synth_scope }; + let source_info = this.source_info(expr_span); let size = this.temp(tcx.types.usize, expr_span); this.cfg.push_assign( block, - synth_info, + source_info, size, Rvalue::NullaryOp(NullOp::SizeOf, value_ty), ); @@ -138,7 +131,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let align = this.temp(tcx.types.usize, expr_span); this.cfg.push_assign( block, - synth_info, + source_info, align, Rvalue::NullaryOp(NullOp::AlignOf, value_ty), ); @@ -154,7 +147,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let success = this.cfg.start_new_block(); this.cfg.terminate( block, - synth_info, + source_info, TerminatorKind::Call { func: exchange_malloc, args: vec![ @@ -580,7 +573,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { result_value, Rvalue::CheckedBinaryOp(op, Box::new((lhs.to_copy(), rhs.to_copy()))), ); - let val_fld = FieldIdx::new(0); + let val_fld = FieldIdx::ZERO; let of_fld = FieldIdx::new(1); let tcx = self.tcx; diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index b4eeeccc127b4..c8360b6a5fdd2 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -69,7 +69,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // FIXME: Does this need extra logic to handle let-chains? let source_info = if this.is_let(cond) { let variable_scope = - this.new_source_scope(then_span, LintLevel::Inherited, None); + this.new_source_scope(then_span, LintLevel::Inherited); this.source_scope = variable_scope; SourceInfo { span: then_span, scope: variable_scope } } else { diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 6d083e66a9b25..367c391b45a49 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -14,6 +14,7 @@ use rustc_data_structures::{ fx::{FxHashSet, FxIndexMap, FxIndexSet}, stack::ensure_sufficient_stack, }; +use rustc_hir::{BindingAnnotation, ByRef}; use rustc_middle::middle::region; use rustc_middle::mir::{self, *}; use rustc_middle::thir::{self, *}; @@ -213,12 +214,77 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// /// ## False edges /// - /// We don't want to have the exact structure of the decision tree be - /// visible through borrow checking. False edges ensure that the CFG as - /// seen by borrow checking doesn't encode this. False edges are added: + /// We don't want to have the exact structure of the decision tree be visible through borrow + /// checking. Specifically we want borrowck to think that: + /// - at any point, any or none of the patterns and guards seen so far may have been tested; + /// - after the match, any of the patterns may have matched. /// - /// * From each pre-binding block to the next pre-binding block. - /// * From each otherwise block to the next pre-binding block. + /// For example, all of these would fail to error if borrowck could see the real CFG (examples + /// taken from `tests/ui/nll/match-cfg-fake-edges.rs`): + /// ```ignore (too many errors, this is already in the test suite) + /// let x = String::new(); + /// let _ = match true { + /// _ => {}, + /// _ => drop(x), + /// }; + /// // Borrowck must not know the second arm is never run. + /// drop(x); //~ ERROR use of moved value + /// + /// let x; + /// # let y = true; + /// match y { + /// _ if { x = 2; true } => {}, + /// // Borrowck must not know the guard is always run. + /// _ => drop(x), //~ ERROR used binding `x` is possibly-uninitialized + /// }; + /// + /// let x = String::new(); + /// # let y = true; + /// match y { + /// false if { drop(x); true } => {}, + /// // Borrowck must not know the guard is not run in the `true` case. + /// true => drop(x), //~ ERROR use of moved value: `x` + /// false => {}, + /// }; + /// + /// # let mut y = (true, true); + /// let r = &mut y.1; + /// match y { + /// //~^ ERROR cannot use `y.1` because it was mutably borrowed + /// (false, true) => {} + /// // Borrowck must not know we don't test `y.1` when `y.0` is `true`. + /// (true, _) => drop(r), + /// (false, _) => {} + /// }; + /// ``` + /// + /// We add false edges to act as if we were naively matching each arm in order. What we need is + /// a (fake) path from each candidate to the next, specifically from candidate C's pre-binding + /// block to next candidate D's pre-binding block. For maximum precision (needed for deref + /// patterns), we choose the earliest node on D's success path that doesn't also lead to C (to + /// avoid loops). + /// + /// This turns out to be easy to compute: that block is the `start_block` of the first call to + /// `match_candidates` where D is the first candidate in the list. + /// + /// For example: + /// ```rust + /// # let (x, y) = (true, true); + /// match (x, y) { + /// (true, true) => 1, + /// (false, true) => 2, + /// (true, false) => 3, + /// _ => 4, + /// } + /// # ; + /// ``` + /// In this example, the pre-binding block of arm 1 has a false edge to the block for result + /// `false` of the first test on `x`. The other arms have false edges to the pre-binding blocks + /// of the next arm. + /// + /// On top of this, we also add a false edge from the otherwise_block of each guard to the + /// aforementioned start block of the next candidate, to ensure borrock doesn't rely on which + /// guards may have run. #[instrument(level = "debug", skip(self, arms))] pub(crate) fn match_expr( &mut self, @@ -364,7 +430,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { for candidate in candidates { candidate.visit_leaves(|leaf_candidate| { if let Some(ref mut prev) = previous_candidate { - prev.next_candidate_pre_binding_block = leaf_candidate.pre_binding_block; + assert!(leaf_candidate.false_edge_start_block.is_some()); + prev.next_candidate_start_block = leaf_candidate.false_edge_start_block; } previous_candidate = Some(leaf_candidate); }); @@ -554,7 +621,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) -> BlockAnd<()> { match irrefutable_pat.kind { // Optimize the case of `let x = ...` to write directly into `x` - PatKind::Binding { mode: BindingMode::ByValue, var, subpattern: None, .. } => { + PatKind::Binding { + mode: BindingAnnotation(ByRef::No, _), + var, + subpattern: None, + .. + } => { let place = self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true); unpack!(block = self.expr_into_dest(place, block, initializer_id)); @@ -580,7 +652,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { box Pat { kind: PatKind::Binding { - mode: BindingMode::ByValue, var, subpattern: None, .. + mode: BindingAnnotation(ByRef::No, _), + var, + subpattern: None, + .. }, .. }, @@ -720,17 +795,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.visit_primary_bindings( pattern, UserTypeProjections::none(), - &mut |this, mutability, name, mode, var, span, ty, user_ty| { + &mut |this, name, mode, var, span, ty, user_ty| { if visibility_scope.is_none() { visibility_scope = - Some(this.new_source_scope(scope_span, LintLevel::Inherited, None)); + Some(this.new_source_scope(scope_span, LintLevel::Inherited)); } let source_info = SourceInfo { span, scope: this.source_scope }; let visibility_scope = visibility_scope.unwrap(); this.declare_binding( source_info, visibility_scope, - mutability, name, mode, var, @@ -818,9 +892,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { pattern_user_ty: UserTypeProjections, f: &mut impl FnMut( &mut Self, - Mutability, Symbol, - BindingMode, + BindingAnnotation, LocalVarId, Span, Ty<'tcx>, @@ -832,18 +905,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { pattern, pattern_user_ty ); match pattern.kind { - PatKind::Binding { - mutability, - name, - mode, - var, - ty, - ref subpattern, - is_primary, - .. - } => { + PatKind::Binding { name, mode, var, ty, ref subpattern, is_primary, .. } => { if is_primary { - f(self, mutability, name, mode, var, pattern.span, ty, pattern_user_ty.clone()); + f(self, name, mode, var, pattern.span, ty, pattern_user_ty.clone()); } if let Some(subpattern) = subpattern.as_ref() { self.visit_primary_bindings(subpattern, pattern_user_ty, f); @@ -1012,8 +1076,12 @@ struct Candidate<'pat, 'tcx> { /// The block before the `bindings` have been established. pre_binding_block: Option, - /// The pre-binding block of the next candidate. - next_candidate_pre_binding_block: Option, + + /// The earliest block that has only candidates >= this one as descendents. Used for false + /// edges, see the doc for [`Builder::match_expr`]. + false_edge_start_block: Option, + /// The `false_edge_start_block` of the next candidate. + next_candidate_start_block: Option, } impl<'tcx, 'pat> Candidate<'pat, 'tcx> { @@ -1035,7 +1103,8 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> { or_span: None, otherwise_block: None, pre_binding_block: None, - next_candidate_pre_binding_block: None, + false_edge_start_block: None, + next_candidate_start_block: None, } } @@ -1079,7 +1148,7 @@ struct Binding<'tcx> { span: Span, source: Place<'tcx>, var_id: LocalVarId, - binding_mode: BindingMode, + binding_mode: BindingAnnotation, } /// Indicates that the type of `source` must be a subtype of the @@ -1272,9 +1341,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) { let mut split_or_candidate = false; for candidate in &mut *candidates { - if let [MatchPair { test_case: TestCase::Or { pats, .. }, .. }] = - &*candidate.match_pairs - { + if let [MatchPair { test_case: TestCase::Or { .. }, .. }] = &*candidate.match_pairs { // Split a candidate in which the only match-pair is an or-pattern into multiple // candidates. This is so that // @@ -1284,9 +1351,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // } // // only generates a single switch. - candidate.subcandidates = self.create_or_subcandidates(pats, candidate.has_guard); - let first_match_pair = candidate.match_pairs.pop().unwrap(); - candidate.or_span = Some(first_match_pair.pattern.span); + let match_pair = candidate.match_pairs.pop().unwrap(); + self.create_or_subcandidates(candidate, match_pair); split_or_candidate = true; } } @@ -1299,7 +1365,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { for candidate in candidates.iter_mut() { candidate.visit_leaves(|leaf_candidate| new_candidates.push(leaf_candidate)); } - self.match_simplified_candidates( + self.match_candidates( span, scrutinee_span, start_block, @@ -1330,6 +1396,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { otherwise_block: BasicBlock, candidates: &mut [&mut Candidate<'_, 'tcx>], ) { + if let [first, ..] = candidates { + if first.false_edge_start_block.is_none() { + first.false_edge_start_block = Some(start_block); + } + } + match candidates { [] => { // If there are no candidates that still need testing, we're done. Since all matches are @@ -1474,14 +1546,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { return; } - let match_pairs = mem::take(&mut first_candidate.match_pairs); - let (first_match_pair, remaining_match_pairs) = match_pairs.split_first().unwrap(); - let TestCase::Or { ref pats } = &first_match_pair.test_case else { unreachable!() }; - + let first_match_pair = first_candidate.match_pairs.remove(0); + let remaining_match_pairs = mem::take(&mut first_candidate.match_pairs); let remainder_start = self.cfg.start_new_block(); - let or_span = first_match_pair.pattern.span; // Test the alternatives of this or-pattern. - self.test_or_pattern(first_candidate, start_block, remainder_start, pats, or_span); + self.test_or_pattern(first_candidate, start_block, remainder_start, first_match_pair); if !remaining_match_pairs.is_empty() { // If more match pairs remain, test them after each subcandidate. @@ -1516,25 +1585,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); } - #[instrument( - skip(self, start_block, otherwise_block, or_span, candidate, pats), - level = "debug" - )] + #[instrument(skip(self, start_block, otherwise_block, candidate, match_pair), level = "debug")] fn test_or_pattern<'pat>( &mut self, candidate: &mut Candidate<'pat, 'tcx>, start_block: BasicBlock, otherwise_block: BasicBlock, - pats: &[FlatPat<'pat, 'tcx>], - or_span: Span, + match_pair: MatchPair<'pat, 'tcx>, ) { - debug!("candidate={:#?}\npats={:#?}", candidate, pats); - let mut or_candidates: Vec<_> = pats - .iter() - .cloned() - .map(|flat_pat| Candidate::from_flat_pat(flat_pat, candidate.has_guard)) - .collect(); - let mut or_candidate_refs: Vec<_> = or_candidates.iter_mut().collect(); + let or_span = match_pair.pattern.span; + self.create_or_subcandidates(candidate, match_pair); + let mut or_candidate_refs: Vec<_> = candidate.subcandidates.iter_mut().collect(); self.match_candidates( or_span, or_span, @@ -1542,34 +1603,49 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { otherwise_block, &mut or_candidate_refs, ); - candidate.subcandidates = or_candidates; - candidate.or_span = Some(or_span); self.merge_trivial_subcandidates(candidate); } - /// Try to merge all of the subcandidates of the given candidate into one. - /// This avoids exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`. + /// Given a match-pair that corresponds to an or-pattern, expand each subpattern into a new + /// subcandidate. Any candidate that has been expanded that way should be passed to + /// `merge_trivial_subcandidates` after its subcandidates have been processed. + fn create_or_subcandidates<'pat>( + &mut self, + candidate: &mut Candidate<'pat, 'tcx>, + match_pair: MatchPair<'pat, 'tcx>, + ) { + let TestCase::Or { pats } = match_pair.test_case else { bug!() }; + debug!("expanding or-pattern: candidate={:#?}\npats={:#?}", candidate, pats); + candidate.or_span = Some(match_pair.pattern.span); + candidate.subcandidates = pats + .into_vec() + .into_iter() + .map(|flat_pat| Candidate::from_flat_pat(flat_pat, candidate.has_guard)) + .collect(); + candidate.subcandidates[0].false_edge_start_block = candidate.false_edge_start_block; + } + + /// Try to merge all of the subcandidates of the given candidate into one. This avoids + /// exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`. The or-pattern should have + /// been expanded with `create_or_subcandidates`. fn merge_trivial_subcandidates(&mut self, candidate: &mut Candidate<'_, 'tcx>) { if candidate.subcandidates.is_empty() || candidate.has_guard { // FIXME(or_patterns; matthewjasper) Don't give up if we have a guard. return; } - let mut can_merge = true; - - // Not `Iterator::all` because we don't want to short-circuit. - for subcandidate in &mut candidate.subcandidates { - self.merge_trivial_subcandidates(subcandidate); - - // FIXME(or_patterns; matthewjasper) Try to be more aggressive here. - can_merge &= - subcandidate.subcandidates.is_empty() && subcandidate.extra_data.is_empty(); - } - + // FIXME(or_patterns; matthewjasper) Try to be more aggressive here. + let can_merge = candidate.subcandidates.iter().all(|subcandidate| { + subcandidate.subcandidates.is_empty() && subcandidate.extra_data.is_empty() + }); if can_merge { let any_matches = self.cfg.start_new_block(); let or_span = candidate.or_span.take().unwrap(); let source_info = self.source_info(or_span); + if candidate.false_edge_start_block.is_none() { + candidate.false_edge_start_block = + candidate.subcandidates[0].false_edge_start_block; + } for subcandidate in mem::take(&mut candidate.subcandidates) { let or_block = subcandidate.pre_binding_block.unwrap(); self.cfg.goto(or_block, source_info, any_matches); @@ -1595,10 +1671,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// [`Switch`]: TestKind::Switch /// [`SwitchInt`]: TestKind::SwitchInt /// [`Range`]: TestKind::Range - fn pick_test( - &mut self, - candidates: &mut [&mut Candidate<'_, 'tcx>], - ) -> (Place<'tcx>, Test<'tcx>) { + fn pick_test(&mut self, candidates: &[&mut Candidate<'_, 'tcx>]) -> (Place<'tcx>, Test<'tcx>) { // Extract the match-pair from the highest priority candidate let match_pair = &candidates.first().unwrap().match_pairs[0]; let test = self.test(match_pair); @@ -1988,12 +2061,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let mut block = candidate.pre_binding_block.unwrap(); - if candidate.next_candidate_pre_binding_block.is_some() { + if candidate.next_candidate_start_block.is_some() { let fresh_block = self.cfg.start_new_block(); self.false_edges( block, fresh_block, - candidate.next_candidate_pre_binding_block, + candidate.next_candidate_start_block, candidate_source_info, ); block = fresh_block; @@ -2097,9 +2170,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { parent_data.iter().flat_map(|d| &d.bindings).chain(&candidate.extra_data.bindings); self.bind_matched_candidate_for_guard(block, schedule_drops, bindings.clone()); - let guard_frame = GuardFrame { - locals: bindings.map(|b| GuardFrameLocal::new(b.var_id, b.binding_mode)).collect(), - }; + let guard_frame = + GuardFrame { locals: bindings.map(|b| GuardFrameLocal::new(b.var_id)).collect() }; debug!("entering guard building context: {:?}", guard_frame); self.guard_context.push(guard_frame); @@ -2142,7 +2214,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.false_edges( otherwise_post_guard_block, otherwise_block, - candidate.next_candidate_pre_binding_block, + candidate.next_candidate_start_block, source_info, ); @@ -2176,7 +2248,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { .iter() .flat_map(|d| &d.bindings) .chain(&candidate.extra_data.bindings) - .filter(|binding| matches!(binding.binding_mode, BindingMode::ByValue)); + .filter(|binding| matches!(binding.binding_mode.0, ByRef::No)); // Read all of the by reference bindings to ensure that the // place they refer to can't be modified by the guard. for binding in by_value_bindings.clone() { @@ -2263,12 +2335,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { RefWithinGuard, schedule_drops, ); - match binding.binding_mode { - BindingMode::ByValue => { + match binding.binding_mode.0 { + ByRef::No => { let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, binding.source); self.cfg.push_assign(block, source_info, ref_for_guard, rvalue); } - BindingMode::ByRef(borrow_kind) => { + ByRef::Yes(mutbl) => { let value_for_arm = self.storage_live_binding( block, binding.var_id, @@ -2277,7 +2349,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { schedule_drops, ); - let rvalue = Rvalue::Ref(re_erased, borrow_kind, binding.source); + let rvalue = + Rvalue::Ref(re_erased, util::ref_pat_borrow_kind(mutbl), binding.source); self.cfg.push_assign(block, source_info, value_for_arm, rvalue); let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, value_for_arm); self.cfg.push_assign(block, source_info, ref_for_guard, rvalue); @@ -2318,10 +2391,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if schedule_drops { self.schedule_drop_for_binding(binding.var_id, binding.span, OutsideGuard); } - let rvalue = match binding.binding_mode { - BindingMode::ByValue => Rvalue::Use(self.consume_by_copy_or_move(binding.source)), - BindingMode::ByRef(borrow_kind) => { - Rvalue::Ref(re_erased, borrow_kind, binding.source) + let rvalue = match binding.binding_mode.0 { + ByRef::No => Rvalue::Use(self.consume_by_copy_or_move(binding.source)), + ByRef::Yes(mutbl) => { + Rvalue::Ref(re_erased, util::ref_pat_borrow_kind(mutbl), binding.source) } }; self.cfg.push_assign(block, source_info, local, rvalue); @@ -2338,9 +2411,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, source_info: SourceInfo, visibility_scope: SourceScope, - mutability: Mutability, name: Symbol, - mode: BindingMode, + mode: BindingAnnotation, var_id: LocalVarId, var_ty: Ty<'tcx>, user_ty: UserTypeProjections, @@ -2350,18 +2422,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) { let tcx = self.tcx; let debug_source_info = SourceInfo { span: source_info.span, scope: visibility_scope }; - let binding_mode = match mode { - BindingMode::ByValue => ty::BindingMode::BindByValue(mutability), - BindingMode::ByRef(_) => ty::BindingMode::BindByReference(mutability), - }; let local = LocalDecl { - mutability, + mutability: mode.1, ty: var_ty, user_ty: if user_ty.is_empty() { None } else { Some(Box::new(user_ty)) }, source_info, local_info: ClearCrossCrate::Set(Box::new(LocalInfo::User(BindingForm::Var( VarBindingForm { - binding_mode, + binding_mode: mode, // hypothetically, `visit_primary_bindings` could try to unzip // an outermost hir::Ty as we descend, matching up // idents in pat; but complex w/ unclear UI payoff. diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs index bf1906f370b64..90f12e55ff4c7 100644 --- a/compiler/rustc_mir_build/src/build/matches/simplify.rs +++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs @@ -12,7 +12,7 @@ //! sort of test: for example, testing which variant an enum is, or //! testing a value against a constant. -use crate::build::matches::{Candidate, FlatPat, MatchPair, PatternExtraData, TestCase}; +use crate::build::matches::{MatchPair, PatternExtraData, TestCase}; use crate::build::Builder; use std::mem; @@ -66,27 +66,4 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { match_pairs.sort_by_key(|pair| matches!(pair.test_case, TestCase::Or { .. })); debug!(simplified = ?match_pairs, "simplify_match_pairs"); } - - /// Create a new candidate for each pattern in `pats`, and recursively simplify tje - /// single-or-pattern case. - pub(super) fn create_or_subcandidates<'pat>( - &mut self, - pats: &[FlatPat<'pat, 'tcx>], - has_guard: bool, - ) -> Vec> { - pats.iter() - .cloned() - .map(|flat_pat| { - let mut candidate = Candidate::from_flat_pat(flat_pat, has_guard); - if let [MatchPair { test_case: TestCase::Or { pats, .. }, .. }] = - &*candidate.match_pairs - { - candidate.subcandidates = self.create_or_subcandidates(pats, has_guard); - let first_match_pair = candidate.match_pairs.pop().unwrap(); - candidate.or_span = Some(first_match_pair.pattern.span); - } - candidate - }) - .collect() - } } diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index b66dd83b7ecbb..690879b94885a 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -650,12 +650,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - // FIXME(#29623): return `Some(1)` when the values are different. - (TestKind::Eq { value: test_val, .. }, TestCase::Constant { value: case_val }) - if test_val == case_val => - { - fully_matched = true; - Some(TestBranch::Success) + (TestKind::Eq { value: test_val, .. }, TestCase::Constant { value: case_val }) => { + if test_val == case_val { + fully_matched = true; + Some(TestBranch::Success) + } else { + fully_matched = false; + Some(TestBranch::Failure) + } } ( diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/build/matches/util.rs index bc6f0a26582c8..440be873d4ee7 100644 --- a/compiler/rustc_mir_build/src/build/matches/util.rs +++ b/compiler/rustc_mir_build/src/build/matches/util.rs @@ -154,15 +154,7 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> { TestCase::Irrefutable { ascription, binding: None } } - PatKind::Binding { - name: _, - mutability: _, - mode, - var, - ty: _, - ref subpattern, - is_primary: _, - } => { + PatKind::Binding { mode, var, ref subpattern, .. } => { let binding = place.map(|source| super::Binding { span: pattern.span, source, @@ -347,3 +339,11 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> { } } } + +#[must_use] +pub fn ref_pat_borrow_kind(ref_mutability: Mutability) -> BorrowKind { + match ref_mutability { + Mutability::Mut => BorrowKind::Mut { kind: MutBorrowKind::Default }, + Mutability::Not => BorrowKind::Shared, + } +} diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index a43aadab4785e..6972bc00e0b2e 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -7,10 +7,9 @@ use rustc_ast::attr; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sorted_map::SortedIndexMultiMap; use rustc_errors::ErrorGuaranteed; -use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::Node; +use rustc_hir::{self as hir, BindingAnnotation, ByRef, Node}; use rustc_index::bit_set::GrowableBitSet; use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; @@ -19,9 +18,7 @@ use rustc_middle::middle::region; use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::*; use rustc_middle::query::TyCtxtAt; -use rustc_middle::thir::{ - self, BindingMode, ExprId, LintLevel, LocalVarId, Param, ParamId, PatKind, Thir, -}; +use rustc_middle::thir::{self, ExprId, LintLevel, LocalVarId, Param, ParamId, PatKind, Thir}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::symbol::sym; use rustc_span::Span; @@ -69,17 +66,10 @@ pub(crate) fn mir_build<'tcx>(tcx: TyCtxtAt<'tcx>, def: LocalDefId) -> Body<'tcx // maybe move the check to a MIR pass? tcx.ensure().check_liveness(def); - if tcx.sess.opts.unstable_opts.thir_unsafeck { - // Don't steal here if THIR unsafeck is being used. Instead - // steal in unsafeck. This is so that pattern inline constants - // can be evaluated as part of building the THIR of the parent - // function without a cycle. - build_mir(&thir.borrow()) - } else { - // We ran all queries that depended on THIR at the beginning - // of `mir_build`, so now we can steal it - build_mir(&thir.steal()) - } + // Don't steal here, instead steal in unsafeck. This is so that + // pattern inline constants can be evaluated as part of building the + // THIR of the parent function without a cycle. + build_mir(&thir.borrow()) } }; @@ -193,9 +183,6 @@ struct Builder<'a, 'tcx> { /// `{ STMTS; EXPR1 } + EXPR2`. block_context: BlockContext, - /// The current unsafe block in scope - in_scope_unsafe: Safety, - /// The vector of all scopes that we have created thus far; /// we track this for debuginfo later. source_scopes: IndexVec>, @@ -337,7 +324,7 @@ struct GuardFrameLocal { } impl GuardFrameLocal { - fn new(id: LocalVarId, _binding_mode: BindingMode) -> Self { + fn new(id: LocalVarId) -> Self { GuardFrameLocal { id } } } @@ -473,11 +460,6 @@ fn construct_fn<'tcx>( .output .span(); - let safety = match fn_sig.unsafety { - hir::Unsafety::Normal => Safety::Safe, - hir::Unsafety::Unsafe => Safety::FnUnsafe, - }; - let mut abi = fn_sig.abi; if let DefKind::Closure = tcx.def_kind(fn_def) { // HACK(eddyb) Avoid having RustCall on closures, @@ -523,7 +505,6 @@ fn construct_fn<'tcx>( fn_id, span_with_body, arguments.len(), - safety, return_ty, return_ty_span, coroutine, @@ -593,18 +574,8 @@ fn construct_const<'a, 'tcx>( }; let infcx = tcx.infer_ctxt().build(); - let mut builder = Builder::new( - thir, - infcx, - def, - hir_id, - span, - 0, - Safety::Safe, - const_ty, - const_ty_span, - None, - ); + let mut builder = + Builder::new(thir, infcx, def, hir_id, span, 0, const_ty, const_ty_span, None); let mut block = START_BLOCK; unpack!(block = builder.expr_into_dest(Place::return_place(), block, expr)); @@ -726,10 +697,7 @@ fn construct_error(tcx: TyCtxt<'_>, def_id: LocalDefId, guar: ErrorGuaranteed) - parent_scope: None, inlined: None, inlined_parent_scope: None, - local_data: ClearCrossCrate::Set(SourceScopeLocalData { - lint_root: hir_id, - safety: Safety::Safe, - }), + local_data: ClearCrossCrate::Set(SourceScopeLocalData { lint_root: hir_id }), }); cfg.terminate(START_BLOCK, source_info, TerminatorKind::Unreachable); @@ -756,7 +724,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { hir_id: hir::HirId, span: Span, arg_count: usize, - safety: Safety, return_ty: Ty<'tcx>, return_span: Span, coroutine: Option>>, @@ -798,7 +765,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { guard_context: vec![], fixed_temps: Default::default(), fixed_temps_scope: None, - in_scope_unsafe: safety, local_decls: IndexVec::from_elem_n(LocalDecl::new(return_ty, return_span), 1), canonical_user_type_annotations: IndexVec::new(), upvars: CaptureMap::new(), @@ -810,10 +776,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }; assert_eq!(builder.cfg.start_new_block(), START_BLOCK); - assert_eq!( - builder.new_source_scope(span, lint_level, Some(safety)), - OUTERMOST_SOURCE_SCOPE - ); + assert_eq!(builder.new_source_scope(span, lint_level), OUTERMOST_SOURCE_SCOPE); builder.source_scopes[OUTERMOST_SOURCE_SCOPE].parent_scope = None; builder @@ -967,9 +930,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { match pat.kind { // Don't introduce extra copies for simple bindings PatKind::Binding { - mutability, var, - mode: BindingMode::ByValue, + mode: BindingAnnotation(ByRef::No, mutability), subpattern: None, .. } => { @@ -979,7 +941,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if let Some(kind) = param.self_kind { LocalInfo::User(BindingForm::ImplicitSelf(kind)) } else { - let binding_mode = ty::BindingMode::BindByValue(mutability); + let binding_mode = BindingAnnotation(ByRef::No, mutability); LocalInfo::User(BindingForm::Var(VarBindingForm { binding_mode, opt_ty_info: param.ty_span, @@ -1028,7 +990,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { .as_ref() .assert_crate_local() .lint_root; - self.maybe_new_source_scope(pattern_span, None, arg_hir_id, parent_id); + self.maybe_new_source_scope(pattern_span, arg_hir_id, parent_id); } fn get_unit_temp(&mut self) -> Place<'tcx> { diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index aef63896dde1e..2d31e84aba7da 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -190,7 +190,7 @@ rustc_index::newtype_index! { struct DropIdx {} } -const ROOT_NODE: DropIdx = DropIdx::from_u32(0); +const ROOT_NODE: DropIdx = DropIdx::ZERO; /// A tree of drops that we have deferred lowering. It's used for: /// @@ -578,7 +578,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if let LintLevel::Explicit(current_hir_id) = lint_level { let parent_id = self.source_scopes[source_scope].local_data.as_ref().assert_crate_local().lint_root; - self.maybe_new_source_scope(region_scope.1.span, None, current_hir_id, parent_id); + self.maybe_new_source_scope(region_scope.1.span, current_hir_id, parent_id); } self.push_scope(region_scope); let mut block; @@ -767,7 +767,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { pub(crate) fn maybe_new_source_scope( &mut self, span: Span, - safety: Option, current_id: HirId, parent_id: HirId, ) { @@ -797,7 +796,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if current_root != parent_root { let lint_level = LintLevel::Explicit(current_root); - self.source_scope = self.new_source_scope(span, lint_level, safety); + self.source_scope = self.new_source_scope(span, lint_level); } } @@ -846,18 +845,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } /// Creates a new source scope, nested in the current one. - pub(crate) fn new_source_scope( - &mut self, - span: Span, - lint_level: LintLevel, - safety: Option, - ) -> SourceScope { + pub(crate) fn new_source_scope(&mut self, span: Span, lint_level: LintLevel) -> SourceScope { let parent = self.source_scope; debug!( - "new_source_scope({:?}, {:?}, {:?}) - parent({:?})={:?}", + "new_source_scope({:?}, {:?}) - parent({:?})={:?}", span, lint_level, - safety, parent, self.source_scopes.get(parent) ); @@ -867,9 +860,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } else { self.source_scopes[parent].local_data.as_ref().assert_crate_local().lint_root }, - safety: safety.unwrap_or_else(|| { - self.source_scopes[parent].local_data.as_ref().assert_crate_local().safety - }), }; self.source_scopes.push(SourceScopeData { span, diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index e04fe31a76f2b..8aa9a75d96af0 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -2,11 +2,11 @@ use std::borrow::Cow; use crate::build::ExprCategory; use crate::errors::*; -use rustc_middle::thir::visit::Visitor; use rustc_errors::DiagArgValue; -use rustc_hir as hir; +use rustc_hir::{self as hir, BindingAnnotation, ByRef, Mutability}; use rustc_middle::mir::BorrowKind; +use rustc_middle::thir::visit::Visitor; use rustc_middle::thir::*; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt}; @@ -289,22 +289,22 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { visit::walk_pat(self, pat); } } - PatKind::Binding { mode: BindingMode::ByRef(borrow_kind), ty, .. } => { + PatKind::Binding { mode: BindingAnnotation(ByRef::Yes(rm), _), ty, .. } => { if self.inside_adt { let ty::Ref(_, ty, _) = ty.kind() else { span_bug!( pat.span, - "BindingMode::ByRef in pattern, but found non-reference type {}", + "ByRef::Yes in pattern, but found non-reference type {}", ty ); }; - match borrow_kind { - BorrowKind::Fake | BorrowKind::Shared => { + match rm { + Mutability::Not => { if !ty.is_freeze(self.tcx, self.param_env) { self.requires_unsafe(pat.span, BorrowOfLayoutConstrainedField); } } - BorrowKind::Mut { .. } => { + Mutability::Mut { .. } => { self.requires_unsafe(pat.span, MutationOfLayoutConstrainedField); } } @@ -909,11 +909,6 @@ impl UnsafeOpKind { } pub fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) { - // THIR unsafeck can be disabled with `-Z thir-unsafeck=off` - if !tcx.sess.opts.unstable_opts.thir_unsafeck { - return; - } - // Closures and inline consts are handled by their owner, if it has a body // Also, don't safety check custom MIR if tcx.is_typeck_child(def.to_def_id()) || tcx.has_attr(def, sym::custom_mir) { diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 848da56f98168..26f10fdd333cb 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -456,8 +456,8 @@ pub enum UnusedUnsafeEnclosing { pub(crate) struct NonExhaustivePatternsTypeNotEmpty<'p, 'tcx, 'm> { pub cx: &'m RustcPatCtxt<'p, 'tcx>, - pub expr_span: Span, - pub span: Span, + pub scrut_span: Span, + pub braces_span: Option, pub ty: Ty<'tcx>, } @@ -465,7 +465,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for NonExhaustivePatternsTypeNo fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'_, G> { let mut diag = Diag::new(dcx, level, fluent::mir_build_non_exhaustive_patterns_type_not_empty); - diag.span(self.span); + diag.span(self.scrut_span); diag.code(E0004); let peeled_ty = self.ty.peel_refs(); diag.arg("ty", self.ty); @@ -502,26 +502,19 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for NonExhaustivePatternsTypeNo } } - let mut suggestion = None; let sm = self.cx.tcx.sess.source_map(); - if self.span.eq_ctxt(self.expr_span) { + if let Some(braces_span) = self.braces_span { // Get the span for the empty match body `{}`. - let (indentation, more) = if let Some(snippet) = sm.indentation_before(self.span) { + let (indentation, more) = if let Some(snippet) = sm.indentation_before(self.scrut_span) + { (format!("\n{snippet}"), " ") } else { (" ".to_string(), "") }; - suggestion = Some(( - self.span.shrink_to_hi().with_hi(self.expr_span.hi()), - format!(" {{{indentation}{more}_ => todo!(),{indentation}}}",), - )); - } - - if let Some((span, sugg)) = suggestion { diag.span_suggestion_verbose( - span, + braces_span, fluent::mir_build_suggestion, - sugg, + format!(" {{{indentation}{more}_ => todo!(),{indentation}}}"), Applicability::HasPlaceholders, ); } else { diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 1e508ffc1e75b..c697e16217bd4 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -716,10 +716,11 @@ impl<'tcx> Cx<'tcx> { then: self.mirror_expr(then), else_opt: else_opt.map(|el| self.mirror_expr(el)), }, - hir::ExprKind::Match(discr, arms, _) => ExprKind::Match { + hir::ExprKind::Match(discr, arms, match_source) => ExprKind::Match { scrutinee: self.mirror_expr(discr), scrutinee_hir_id: discr.hir_id, arms: arms.iter().map(|a| self.convert_arm(a)).collect(), + match_source, }, hir::ExprKind::Loop(body, ..) => { let block_ty = self.typeck_results().node_type(body.hir_id); diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 434ed16d5c6ec..a3e6c2abc51fa 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -13,10 +13,9 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{ codes::*, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, MultiSpan, }; -use rustc_hir as hir; use rustc_hir::def::*; use rustc_hir::def_id::LocalDefId; -use rustc_hir::HirId; +use rustc_hir::{self as hir, BindingAnnotation, ByRef, HirId}; use rustc_middle::middle::limits::get_limit_size; use rustc_middle::thir::visit::Visitor; use rustc_middle::thir::*; @@ -145,16 +144,8 @@ impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> { }); return; } - ExprKind::Match { scrutinee, scrutinee_hir_id, box ref arms } => { - let source = match ex.span.desugaring_kind() { - Some(DesugaringKind::ForLoop) => hir::MatchSource::ForLoopDesugar, - Some(DesugaringKind::QuestionMark) => { - hir::MatchSource::TryDesugar(scrutinee_hir_id) - } - Some(DesugaringKind::Await) => hir::MatchSource::AwaitDesugar, - _ => hir::MatchSource::Normal, - }; - self.check_match(scrutinee, arms, source, ex.span); + ExprKind::Match { scrutinee, scrutinee_hir_id: _, box ref arms, match_source } => { + self.check_match(scrutinee, arms, match_source, ex.span); } ExprKind::Let { box ref pat, expr } => { self.check_let(pat, Some(expr), ex.span); @@ -506,8 +497,41 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { None, ); } else { + // span after scrutinee, or after `.match`. That is, the braces, arms, + // and any whitespace preceding the braces. + let braces_span = match source { + hir::MatchSource::Normal => scrut + .span + .find_ancestor_in_same_ctxt(expr_span) + .map(|scrut_span| scrut_span.shrink_to_hi().with_hi(expr_span.hi())), + hir::MatchSource::Postfix => { + // This is horrendous, and we should deal with it by just + // stashing the span of the braces somewhere (like in the match source). + scrut.span.find_ancestor_in_same_ctxt(expr_span).and_then(|scrut_span| { + let sm = self.tcx.sess.source_map(); + let brace_span = sm.span_extend_to_next_char(scrut_span, '{', true); + if sm.span_to_snippet(sm.next_point(brace_span)).as_deref() == Ok("{") { + let sp = brace_span.shrink_to_hi().with_hi(expr_span.hi()); + // We also need to extend backwards for whitespace + sm.span_extend_prev_while(sp, |c| c.is_whitespace()).ok() + } else { + None + } + }) + } + hir::MatchSource::ForLoopDesugar + | hir::MatchSource::TryDesugar(_) + | hir::MatchSource::AwaitDesugar + | hir::MatchSource::FormatArgs => None, + }; self.error = Err(report_non_exhaustive_match( - &cx, self.thir, scrut.ty, scrut.span, witnesses, arms, expr_span, + &cx, + self.thir, + scrut.ty, + scrut.span, + witnesses, + arms, + braces_span, )); } } @@ -723,13 +747,14 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat: let sess = cx.tcx.sess; // Get the binding move, extract the mutability if by-ref. - let mut_outer = match mode { - BindingMode::ByValue if is_binding_by_move(ty) => { + let mut_outer = match mode.0 { + ByRef::No if is_binding_by_move(ty) => { // We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`. let mut conflicts_ref = Vec::new(); - sub.each_binding(|_, mode, _, span| match mode { - BindingMode::ByValue => {} - BindingMode::ByRef(_) => conflicts_ref.push(span), + sub.each_binding(|_, mode, _, span| { + if matches!(mode, ByRef::Yes(_)) { + conflicts_ref.push(span) + } }); if !conflicts_ref.is_empty() { sess.dcx().emit_err(BorrowOfMovedValue { @@ -742,8 +767,8 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat: } return; } - BindingMode::ByValue => return, - BindingMode::ByRef(m) => m.mutability(), + ByRef::No => return, + ByRef::Yes(m) => m, }; // We now have `ref $mut_outer binding @ sub` (semantically). @@ -753,7 +778,7 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat: let mut conflicts_mut_ref = Vec::new(); sub.each_binding(|name, mode, ty, span| { match mode { - BindingMode::ByRef(mut_inner) => match (mut_outer, mut_inner.mutability()) { + ByRef::Yes(mut_inner) => match (mut_outer, mut_inner) { // Both sides are `ref`. (Mutability::Not, Mutability::Not) => {} // 2x `ref mut`. @@ -767,10 +792,10 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat: conflicts_mut_ref.push(Conflict::Ref { span, name }) } }, - BindingMode::ByValue if is_binding_by_move(ty) => { + ByRef::No if is_binding_by_move(ty) => { conflicts_move.push(Conflict::Moved { span, name }) // `ref mut?` + by-move conflict. } - BindingMode::ByValue => {} // `ref mut?` + by-copy is fine. + ByRef::No => {} // `ref mut?` + by-copy is fine. } }); @@ -813,8 +838,7 @@ fn check_for_bindings_named_same_as_variants( ) { if let PatKind::Binding { name, - mode: BindingMode::ByValue, - mutability: Mutability::Not, + mode: BindingAnnotation(ByRef::No, Mutability::Not), subpattern: None, ty, .. @@ -930,7 +954,7 @@ fn report_non_exhaustive_match<'p, 'tcx>( sp: Span, witnesses: Vec>, arms: &[ArmId], - expr_span: Span, + braces_span: Option, ) -> ErrorGuaranteed { let is_empty_match = arms.is_empty(); let non_empty_enum = match scrut_ty.kind() { @@ -942,8 +966,8 @@ fn report_non_exhaustive_match<'p, 'tcx>( if is_empty_match && !non_empty_enum { return cx.tcx.dcx().emit_err(NonExhaustivePatternsTypeNotEmpty { cx, - expr_span, - span: sp, + scrut_span: sp, + braces_span, ty: scrut_ty, }); } @@ -1029,7 +1053,7 @@ fn report_non_exhaustive_match<'p, 'tcx>( let mut suggestion = None; let sm = cx.tcx.sess.source_map(); match arms { - [] if sp.eq_ctxt(expr_span) => { + [] if let Some(braces_span) = braces_span => { // Get the span for the empty match body `{}`. let (indentation, more) = if let Some(snippet) = sm.indentation_before(sp) { (format!("\n{snippet}"), " ") @@ -1037,7 +1061,7 @@ fn report_non_exhaustive_match<'p, 'tcx>( (" ".to_string(), "") }; suggestion = Some(( - sp.shrink_to_hi().with_hi(expr_span.hi()), + braces_span, format!(" {{{indentation}{more}{suggested_arm},{indentation}}}",), )); } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 0a7e9653377be..133cf8e334929 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -9,15 +9,14 @@ use crate::errors::*; use crate::thir::util::UserAnnotatedTyHelpers; use rustc_errors::codes::*; -use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; -use rustc_hir::RangeEnd; +use rustc_hir::{self as hir, RangeEnd}; use rustc_index::Idx; use rustc_middle::mir::interpret::{ErrorHandled, GlobalId, LitToConstError, LitToConstInput}; -use rustc_middle::mir::{self, BorrowKind, Const, Mutability}; +use rustc_middle::mir::{self, Const}; use rustc_middle::thir::{ - Ascription, BindingMode, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary, + Ascription, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary, }; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, TypeVisitableExt}; @@ -66,7 +65,14 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // we wrap the unadjusted pattern in `PatKind::Deref` repeatedly, consuming the // adjustments in *reverse order* (last-in-first-out, so that the last `Deref` inserted // gets the least-dereferenced type). - let unadjusted_pat = self.lower_pattern_unadjusted(pat); + let unadjusted_pat = match pat.kind { + hir::PatKind::Ref(inner, _) + if self.typeck_results.skipped_ref_pats().contains(pat.hir_id) => + { + self.lower_pattern_unadjusted(inner) + } + _ => self.lower_pattern_unadjusted(pat), + }; self.typeck_results.pat_adjustments().get(pat.hir_id).unwrap_or(&vec![]).iter().rev().fold( unadjusted_pat, |pat: Box<_>, ref_ty| { @@ -281,26 +287,16 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { span = span.with_hi(ident_span.hi()); } - let bm = *self + let mode = *self .typeck_results .pat_binding_modes() .get(pat.hir_id) .expect("missing binding mode"); - let (mutability, mode) = match bm { - ty::BindByValue(mutbl) => (mutbl, BindingMode::ByValue), - ty::BindByReference(hir::Mutability::Mut) => ( - Mutability::Not, - BindingMode::ByRef(BorrowKind::Mut { kind: mir::MutBorrowKind::Default }), - ), - ty::BindByReference(hir::Mutability::Not) => { - (Mutability::Not, BindingMode::ByRef(BorrowKind::Shared)) - } - }; // A ref x pattern is the same node used for x, and as such it has // x's type, which is &T, where we want T (the type being matched). let var_ty = ty; - if let ty::BindByReference(_) = bm { + if let hir::ByRef::Yes(_) = mode.0 { if let ty::Ref(_, rty, _) = ty.kind() { ty = *rty; } else { @@ -309,7 +305,6 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { }; PatKind::Binding { - mutability, mode, name: ident.name, var: LocalVarId(id), diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index 16c4248a15969..ef15082a48138 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -635,9 +635,8 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { self.print_pat(subpattern, depth_lvl + 3); print_indented!(self, "}", depth_lvl + 1); } - PatKind::Binding { mutability, name, mode, var, ty, subpattern, is_primary } => { + PatKind::Binding { name, mode, var, ty, subpattern, is_primary } => { print_indented!(self, "Binding {", depth_lvl + 1); - print_indented!(self, format!("mutability: {:?}", mutability), depth_lvl + 2); print_indented!(self, format!("name: {:?}", name), depth_lvl + 2); print_indented!(self, format!("mode: {:?}", mode), depth_lvl + 2); print_indented!(self, format!("var: {:?}", var), depth_lvl + 2); diff --git a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs index 256add3153c9c..d63db6ea8edd8 100644 --- a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs +++ b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs @@ -420,14 +420,14 @@ where ) -> BasicBlock { // drop glue is sent straight to codegen // box cannot be directly dereferenced - let unique_ty = adt.non_enum_variant().fields[FieldIdx::new(0)].ty(self.tcx(), args); + let unique_ty = adt.non_enum_variant().fields[FieldIdx::ZERO].ty(self.tcx(), args); let unique_variant = unique_ty.ty_adt_def().unwrap().non_enum_variant(); - let nonnull_ty = unique_variant.fields[FieldIdx::from_u32(0)].ty(self.tcx(), args); + let nonnull_ty = unique_variant.fields[FieldIdx::ZERO].ty(self.tcx(), args); let ptr_ty = Ty::new_imm_ptr(self.tcx(), args[0].expect_ty()); - let unique_place = self.tcx().mk_place_field(self.place, FieldIdx::new(0), unique_ty); - let nonnull_place = self.tcx().mk_place_field(unique_place, FieldIdx::new(0), nonnull_ty); - let ptr_place = self.tcx().mk_place_field(nonnull_place, FieldIdx::new(0), ptr_ty); + let unique_place = self.tcx().mk_place_field(self.place, FieldIdx::ZERO, unique_ty); + let nonnull_place = self.tcx().mk_place_field(unique_place, FieldIdx::ZERO, nonnull_ty); + let ptr_place = self.tcx().mk_place_field(nonnull_place, FieldIdx::ZERO, ptr_ty); let interior = self.tcx().mk_place_deref(ptr_place); let interior_path = self.elaborator.deref_subpath(self.path); diff --git a/compiler/rustc_mir_transform/messages.ftl b/compiler/rustc_mir_transform/messages.ftl index b8dbdf18db3ea..f9b79d72b0504 100644 --- a/compiler/rustc_mir_transform/messages.ftl +++ b/compiler/rustc_mir_transform/messages.ftl @@ -1,6 +1,4 @@ mir_transform_arithmetic_overflow = this arithmetic operation will overflow -mir_transform_call_to_unsafe_label = call to unsafe function -mir_transform_call_to_unsafe_note = consult the function's documentation for information on how to avoid undefined behavior mir_transform_const_defined_here = `const` item defined here mir_transform_const_modify = attempting to modify a `const` item @@ -11,10 +9,6 @@ mir_transform_const_mut_borrow = taking a mutable reference to a `const` item .note2 = the mutable reference will refer to this temporary, not the original `const` item .note3 = mutable reference created due to call to this method -mir_transform_const_ptr2int_label = cast of pointer to int -mir_transform_const_ptr2int_note = casting pointers to integers in constants -mir_transform_deref_ptr_label = dereference of raw pointer -mir_transform_deref_ptr_note = raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior mir_transform_ffi_unwind_call = call to {$foreign -> [true] foreign function *[false] function pointer @@ -23,56 +17,13 @@ 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_initializing_valid_range_label = initializing type with `rustc_layout_scalar_valid_range` attr -mir_transform_initializing_valid_range_note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior 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} .help = consider using a block (`{"{ ... }"}`) to shrink the value's scope, ending before the suspend point - -mir_transform_mutation_layout_constrained_borrow_label = borrow of layout constrained field with interior mutability -mir_transform_mutation_layout_constrained_borrow_note = references to fields of layout constrained fields lose the constraints. Coupled with interior mutability, the field can be changed to invalid values -mir_transform_mutation_layout_constrained_label = mutation of layout constrained field -mir_transform_mutation_layout_constrained_note = mutating layout constrained fields cannot statically be checked for valid values mir_transform_operation_will_panic = this operation will panic at runtime -mir_transform_requires_unsafe = {$details} is unsafe and requires unsafe {$op_in_unsafe_fn_allowed -> - [true] function or block - *[false] block - } - .not_inherited = items do not inherit unsafety from separate enclosing items - -mir_transform_target_feature_call_help = in order for the call to be safe, the context requires the following additional target {$missing_target_features_count -> - [1] feature - *[count] features - }: {$missing_target_features} - -mir_transform_target_feature_call_label = call to function with `#[target_feature]` -mir_transform_target_feature_call_note = the {$build_target_features} target {$build_target_features_count -> - [1] feature - *[count] features - } being enabled in the build configuration does not remove the requirement to list {$build_target_features_count -> - [1] it - *[count] them - } in `#[target_feature]` - mir_transform_unaligned_packed_ref = reference to packed field is unaligned .note = packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses .note_ub = creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) .help = copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) - -mir_transform_union_access_label = access to union field -mir_transform_union_access_note = the field may not be properly initialized: using uninitialized data will cause undefined behavior -mir_transform_unsafe_op_in_unsafe_fn = {$details} is unsafe and requires unsafe block (error E0133) - .suggestion = consider wrapping the function body in an unsafe block - .note = an unsafe function restricts its caller, but its body is safe by default - -mir_transform_unused_unsafe = unnecessary `unsafe` block - .label = because it's nested under this `unsafe` block - -mir_transform_use_of_asm_label = use of inline assembly -mir_transform_use_of_asm_note = inline assembly is entirely unchecked and can cause undefined behavior -mir_transform_use_of_extern_static_label = use of extern static -mir_transform_use_of_extern_static_note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior -mir_transform_use_of_static_mut_label = use of mutable static -mir_transform_use_of_static_mut_note = mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs deleted file mode 100644 index a0c3de3af5862..0000000000000 --- a/compiler/rustc_mir_transform/src/check_unsafety.rs +++ /dev/null @@ -1,615 +0,0 @@ -use rustc_data_structures::unord::{ExtendUnord, UnordItems, UnordSet}; -use rustc_hir as hir; -use rustc_hir::def::DefKind; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::hir_id::HirId; -use rustc_hir::intravisit; -use rustc_hir::{BlockCheckMode, ExprKind, Node}; -use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor}; -use rustc_middle::mir::*; -use rustc_middle::query::Providers; -use rustc_middle::ty::{self, TyCtxt}; -use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE}; -use rustc_session::lint::Level; - -use std::ops::Bound; - -use crate::errors; - -pub struct UnsafetyChecker<'a, 'tcx> { - body: &'a Body<'tcx>, - body_did: LocalDefId, - violations: Vec, - source_info: SourceInfo, - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - - /// Used `unsafe` blocks in this function. This is used for the "unused_unsafe" lint. - used_unsafe_blocks: UnordSet, -} - -impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { - fn new( - body: &'a Body<'tcx>, - body_did: LocalDefId, - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> Self { - Self { - body, - body_did, - violations: vec![], - source_info: SourceInfo::outermost(body.span), - tcx, - param_env, - used_unsafe_blocks: Default::default(), - } - } -} - -impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> { - fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { - self.source_info = terminator.source_info; - match terminator.kind { - TerminatorKind::Goto { .. } - | TerminatorKind::SwitchInt { .. } - | TerminatorKind::Drop { .. } - | TerminatorKind::Yield { .. } - | TerminatorKind::Assert { .. } - | TerminatorKind::CoroutineDrop - | TerminatorKind::UnwindResume - | TerminatorKind::UnwindTerminate(_) - | TerminatorKind::Return - | TerminatorKind::Unreachable - | TerminatorKind::FalseEdge { .. } - | TerminatorKind::FalseUnwind { .. } => { - // safe (at least as emitted during MIR construction) - } - - TerminatorKind::Call { ref func, .. } => { - let func_ty = func.ty(self.body, self.tcx); - let func_id = - if let ty::FnDef(func_id, _) = func_ty.kind() { Some(func_id) } else { None }; - let sig = func_ty.fn_sig(self.tcx); - if let hir::Unsafety::Unsafe = sig.unsafety() { - self.require_unsafe( - UnsafetyViolationKind::General, - UnsafetyViolationDetails::CallToUnsafeFunction, - ) - } - - if let Some(func_id) = func_id { - self.check_target_features(*func_id); - } - } - - TerminatorKind::InlineAsm { .. } => self.require_unsafe( - UnsafetyViolationKind::General, - UnsafetyViolationDetails::UseOfInlineAssembly, - ), - } - self.super_terminator(terminator, location); - } - - fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { - self.source_info = statement.source_info; - match statement.kind { - StatementKind::Assign(..) - | StatementKind::FakeRead(..) - | StatementKind::SetDiscriminant { .. } - | StatementKind::Deinit(..) - | StatementKind::StorageLive(..) - | StatementKind::StorageDead(..) - | StatementKind::Retag { .. } - | StatementKind::PlaceMention(..) - | StatementKind::Coverage(..) - | StatementKind::Intrinsic(..) - | StatementKind::ConstEvalCounter - | StatementKind::Nop => { - // safe (at least as emitted during MIR construction) - } - // `AscribeUserType` just exists to help MIR borrowck. - // It has no semantics, and everything is already reported by `PlaceMention`. - StatementKind::AscribeUserType(..) => return, - } - self.super_statement(statement, location); - } - - fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { - match rvalue { - Rvalue::Aggregate(box ref aggregate, _) => match aggregate { - &AggregateKind::Array(..) | &AggregateKind::Tuple => {} - &AggregateKind::Adt(adt_did, ..) => { - match self.tcx.layout_scalar_valid_range(adt_did) { - (Bound::Unbounded, Bound::Unbounded) => {} - _ => self.require_unsafe( - UnsafetyViolationKind::General, - UnsafetyViolationDetails::InitializingTypeWith, - ), - } - } - &AggregateKind::Closure(def_id, _) - | &AggregateKind::CoroutineClosure(def_id, _) - | &AggregateKind::Coroutine(def_id, _) => { - let def_id = def_id.expect_local(); - let UnsafetyCheckResult { violations, used_unsafe_blocks, .. } = - self.tcx.mir_unsafety_check_result(def_id); - self.register_violations(violations, used_unsafe_blocks.items().copied()); - } - }, - _ => {} - } - self.super_rvalue(rvalue, location); - } - - fn visit_operand(&mut self, op: &Operand<'tcx>, location: Location) { - if let Operand::Constant(constant) = op { - let maybe_uneval = match constant.const_ { - Const::Val(..) | Const::Ty(_) => None, - Const::Unevaluated(uv, _) => Some(uv), - }; - - if let Some(uv) = maybe_uneval { - if uv.promoted.is_none() { - let def_id = uv.def; - if self.tcx.def_kind(def_id) == DefKind::InlineConst { - let local_def_id = def_id.expect_local(); - let UnsafetyCheckResult { violations, used_unsafe_blocks, .. } = - self.tcx.mir_unsafety_check_result(local_def_id); - self.register_violations(violations, used_unsafe_blocks.items().copied()); - } - } - } - } - self.super_operand(op, location); - } - - fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) { - // On types with `scalar_valid_range`, prevent - // * `&mut x.field` - // * `x.field = y;` - // * `&x.field` if `field`'s type has interior mutability - // because either of these would allow modifying the layout constrained field and - // insert values that violate the layout constraints. - if context.is_mutating_use() || context.is_borrow() { - self.check_mut_borrowing_layout_constrained_field(*place, context.is_mutating_use()); - } - - // Some checks below need the extra meta info of the local declaration. - let decl = &self.body.local_decls[place.local]; - - // Check the base local: it might be an unsafe-to-access static. We only check derefs of the - // temporary holding the static pointer to avoid duplicate errors - // . - if place.projection.first() == Some(&ProjectionElem::Deref) { - // If the projection root is an artificial local that we introduced when - // desugaring `static`, give a more specific error message - // (avoid the general "raw pointer" clause below, that would only be confusing). - if let LocalInfo::StaticRef { def_id, .. } = *decl.local_info() { - if self.tcx.is_mutable_static(def_id) { - self.require_unsafe( - UnsafetyViolationKind::General, - UnsafetyViolationDetails::UseOfMutableStatic, - ); - return; - } else if self.tcx.is_foreign_item(def_id) { - self.require_unsafe( - UnsafetyViolationKind::General, - UnsafetyViolationDetails::UseOfExternStatic, - ); - return; - } - } - } - - // Check for raw pointer `Deref`. - for (base, proj) in place.iter_projections() { - if proj == ProjectionElem::Deref { - let base_ty = base.ty(self.body, self.tcx).ty; - if base_ty.is_unsafe_ptr() { - self.require_unsafe( - UnsafetyViolationKind::General, - UnsafetyViolationDetails::DerefOfRawPointer, - ) - } - } - } - - // Check for union fields. For this we traverse right-to-left, as the last `Deref` changes - // whether we *read* the union field or potentially *write* to it (if this place is being assigned to). - let mut saw_deref = false; - for (base, proj) in place.iter_projections().rev() { - if proj == ProjectionElem::Deref { - saw_deref = true; - continue; - } - - let base_ty = base.ty(self.body, self.tcx).ty; - if base_ty.is_union() { - // If we did not hit a `Deref` yet and the overall place use is an assignment, the - // rules are different. - let assign_to_field = !saw_deref - && matches!( - context, - PlaceContext::MutatingUse( - MutatingUseContext::Store - | MutatingUseContext::Drop - | MutatingUseContext::AsmOutput - ) - ); - // If this is just an assignment, determine if the assigned type needs dropping. - if assign_to_field { - // We have to check the actual type of the assignment, as that determines if the - // old value is being dropped. - let assigned_ty = place.ty(&self.body.local_decls, self.tcx).ty; - if assigned_ty.needs_drop(self.tcx, self.param_env) { - // This would be unsafe, but should be outright impossible since we reject - // such unions. - assert!( - self.tcx.dcx().has_errors().is_some(), - "union fields that need dropping should be impossible: {assigned_ty}" - ); - } - } else { - self.require_unsafe( - UnsafetyViolationKind::General, - UnsafetyViolationDetails::AccessToUnionField, - ) - } - } - } - } -} - -impl<'tcx> UnsafetyChecker<'_, 'tcx> { - fn require_unsafe(&mut self, kind: UnsafetyViolationKind, details: UnsafetyViolationDetails) { - // Violations can turn out to be `UnsafeFn` during analysis, but they should not start out as such. - assert_ne!(kind, UnsafetyViolationKind::UnsafeFn); - - let source_info = self.source_info; - let lint_root = self.body.source_scopes[self.source_info.scope] - .local_data - .as_ref() - .assert_crate_local() - .lint_root; - self.register_violations( - [&UnsafetyViolation { source_info, lint_root, kind, details }], - UnordItems::empty(), - ); - } - - fn register_violations<'a>( - &mut self, - violations: impl IntoIterator, - new_used_unsafe_blocks: UnordItems>, - ) { - let safety = self.body.source_scopes[self.source_info.scope] - .local_data - .as_ref() - .assert_crate_local() - .safety; - match safety { - // `unsafe` blocks are required in safe code - Safety::Safe => violations.into_iter().for_each(|violation| { - match violation.kind { - UnsafetyViolationKind::General => {} - UnsafetyViolationKind::UnsafeFn => { - bug!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context") - } - } - if !self.violations.contains(violation) { - self.violations.push(violation.clone()) - } - }), - // With the RFC 2585, no longer allow `unsafe` operations in `unsafe fn`s - Safety::FnUnsafe => violations.into_iter().for_each(|violation| { - let mut violation = violation.clone(); - violation.kind = UnsafetyViolationKind::UnsafeFn; - if !self.violations.contains(&violation) { - self.violations.push(violation) - } - }), - Safety::BuiltinUnsafe => {} - Safety::ExplicitUnsafe(hir_id) => violations.into_iter().for_each(|_violation| { - self.used_unsafe_blocks.insert(hir_id); - }), - }; - - self.used_unsafe_blocks.extend_unord(new_used_unsafe_blocks); - } - fn check_mut_borrowing_layout_constrained_field( - &mut self, - place: Place<'tcx>, - is_mut_use: bool, - ) { - for (place_base, elem) in place.iter_projections().rev() { - match elem { - // Modifications behind a dereference don't affect the value of - // the pointer. - ProjectionElem::Deref => return, - ProjectionElem::Field(..) => { - let ty = place_base.ty(&self.body.local_decls, self.tcx).ty; - if let ty::Adt(def, _) = ty.kind() { - if self.tcx.layout_scalar_valid_range(def.did()) - != (Bound::Unbounded, Bound::Unbounded) - { - let details = if is_mut_use { - UnsafetyViolationDetails::MutationOfLayoutConstrainedField - - // Check `is_freeze` as late as possible to avoid cycle errors - // with opaque types. - } else if !place - .ty(self.body, self.tcx) - .ty - .is_freeze(self.tcx, self.param_env) - { - UnsafetyViolationDetails::BorrowOfLayoutConstrainedField - } else { - continue; - }; - self.require_unsafe(UnsafetyViolationKind::General, details); - } - } - } - _ => {} - } - } - } - - /// Checks whether calling `func_did` needs an `unsafe` context or not, i.e. whether - /// the called function has target features the calling function hasn't. - fn check_target_features(&mut self, func_did: DefId) { - // Unsafety isn't required on wasm targets. For more information see - // the corresponding check in typeck/src/collect.rs - if self.tcx.sess.target.options.is_like_wasm { - return; - } - - let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features; - // The body might be a constant, so it doesn't have codegen attributes. - let self_features = &self.tcx.body_codegen_attrs(self.body_did.to_def_id()).target_features; - - // Is `callee_features` a subset of `calling_features`? - if !callee_features.iter().all(|feature| self_features.contains(feature)) { - let missing: Vec<_> = callee_features - .iter() - .copied() - .filter(|feature| !self_features.contains(feature)) - .collect(); - let build_enabled = self - .tcx - .sess - .target_features - .iter() - .copied() - .filter(|feature| missing.contains(feature)) - .collect(); - self.require_unsafe( - UnsafetyViolationKind::General, - UnsafetyViolationDetails::CallToFunctionWith { missing, build_enabled }, - ) - } - } -} - -pub(crate) fn provide(providers: &mut Providers) { - *providers = Providers { mir_unsafety_check_result, ..*providers }; -} - -/// Context information for [`UnusedUnsafeVisitor`] traversal, -/// saves (innermost) relevant context -#[derive(Copy, Clone, Debug)] -enum Context { - Safe, - /// in an `unsafe fn` - UnsafeFn, - /// in a *used* `unsafe` block - /// (i.e. a block without unused-unsafe warning) - UnsafeBlock(HirId), -} - -struct UnusedUnsafeVisitor<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - used_unsafe_blocks: &'a UnordSet, - context: Context, - unused_unsafes: &'a mut Vec<(HirId, UnusedUnsafe)>, -} - -impl<'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'_, 'tcx> { - fn visit_block(&mut self, block: &'tcx hir::Block<'tcx>) { - if let hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::UserProvided) = block.rules { - let used = match self.tcx.lint_level_at_node(UNUSED_UNSAFE, block.hir_id) { - (Level::Allow, _) => true, - _ => self.used_unsafe_blocks.contains(&block.hir_id), - }; - let unused_unsafe = match (self.context, used) { - (_, false) => UnusedUnsafe::Unused, - (Context::Safe, true) | (Context::UnsafeFn, true) => { - let previous_context = self.context; - self.context = Context::UnsafeBlock(block.hir_id); - intravisit::walk_block(self, block); - self.context = previous_context; - return; - } - (Context::UnsafeBlock(hir_id), true) => UnusedUnsafe::InUnsafeBlock(hir_id), - }; - self.unused_unsafes.push((block.hir_id, unused_unsafe)); - } - intravisit::walk_block(self, block); - } - - fn visit_inline_const(&mut self, c: &'tcx hir::ConstBlock) { - self.visit_body(self.tcx.hir().body(c.body)) - } - - fn visit_fn( - &mut self, - fk: intravisit::FnKind<'tcx>, - _fd: &'tcx hir::FnDecl<'tcx>, - b: hir::BodyId, - _s: rustc_span::Span, - _id: LocalDefId, - ) { - if matches!(fk, intravisit::FnKind::Closure) { - self.visit_body(self.tcx.hir().body(b)) - } - } -} - -fn check_unused_unsafe( - tcx: TyCtxt<'_>, - def_id: LocalDefId, - used_unsafe_blocks: &UnordSet, -) -> Vec<(HirId, UnusedUnsafe)> { - let body_id = tcx.hir().maybe_body_owned_by(def_id); - - let Some(body_id) = body_id else { - debug!("check_unused_unsafe({:?}) - no body found", def_id); - return vec![]; - }; - - let body = tcx.hir().body(body_id); - let hir_id = tcx.local_def_id_to_hir_id(def_id); - let context = match tcx.hir().fn_sig_by_hir_id(hir_id) { - Some(sig) if sig.header.unsafety == hir::Unsafety::Unsafe => Context::UnsafeFn, - _ => Context::Safe, - }; - - debug!( - "check_unused_unsafe({:?}, context={:?}, body={:?}, used_unsafe_blocks={:?})", - def_id, body, context, used_unsafe_blocks - ); - - let mut unused_unsafes = vec![]; - - let mut visitor = UnusedUnsafeVisitor { - tcx, - used_unsafe_blocks, - context, - unused_unsafes: &mut unused_unsafes, - }; - intravisit::Visitor::visit_body(&mut visitor, body); - - unused_unsafes -} - -fn mir_unsafety_check_result(tcx: TyCtxt<'_>, def: LocalDefId) -> &UnsafetyCheckResult { - debug!("unsafety_violations({:?})", def); - - // N.B., this borrow is valid because all the consumers of - // `mir_built` force this. - let body = &tcx.mir_built(def).borrow(); - - if body.is_custom_mir() || body.tainted_by_errors.is_some() { - return tcx.arena.alloc(UnsafetyCheckResult { - violations: Vec::new(), - used_unsafe_blocks: Default::default(), - unused_unsafes: Some(Vec::new()), - }); - } - - let param_env = tcx.param_env(def); - - let mut checker = UnsafetyChecker::new(body, def, tcx, param_env); - checker.visit_body(body); - - let unused_unsafes = (!tcx.is_typeck_child(def.to_def_id())) - .then(|| check_unused_unsafe(tcx, def, &checker.used_unsafe_blocks)); - - tcx.arena.alloc(UnsafetyCheckResult { - violations: checker.violations, - used_unsafe_blocks: checker.used_unsafe_blocks, - unused_unsafes, - }) -} - -fn report_unused_unsafe(tcx: TyCtxt<'_>, kind: UnusedUnsafe, id: HirId) { - let span = tcx.sess.source_map().guess_head_span(tcx.hir().span(id)); - let nested_parent = if let UnusedUnsafe::InUnsafeBlock(id) = kind { - Some(tcx.sess.source_map().guess_head_span(tcx.hir().span(id))) - } else { - None - }; - tcx.emit_node_span_lint(UNUSED_UNSAFE, id, span, errors::UnusedUnsafe { span, nested_parent }); -} - -pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) { - debug!("check_unsafety({:?})", def_id); - - // closures and inline consts are handled by their parent fn. - if tcx.is_typeck_child(def_id.to_def_id()) { - return; - } - - let UnsafetyCheckResult { violations, unused_unsafes, .. } = - tcx.mir_unsafety_check_result(def_id); - // Only suggest wrapping the entire function body in an unsafe block once - let mut suggest_unsafe_block = true; - - for &UnsafetyViolation { source_info, lint_root, kind, ref details } in violations.iter() { - let details = - errors::RequiresUnsafeDetail { violation: details.clone(), span: source_info.span }; - - match kind { - UnsafetyViolationKind::General => { - let op_in_unsafe_fn_allowed = unsafe_op_in_unsafe_fn_allowed(tcx, lint_root); - let note_non_inherited = tcx.hir().parent_iter(lint_root).find(|(id, node)| { - if let Node::Expr(block) = node - && let ExprKind::Block(block, _) = block.kind - && let BlockCheckMode::UnsafeBlock(_) = block.rules - { - true - } else if let Some(sig) = tcx.hir().fn_sig_by_hir_id(*id) - && sig.header.is_unsafe() - { - true - } else { - false - } - }); - let enclosing = if let Some((id, _)) = note_non_inherited { - Some(tcx.sess.source_map().guess_head_span(tcx.hir().span(id))) - } else { - None - }; - tcx.dcx().emit_err(errors::RequiresUnsafe { - span: source_info.span, - enclosing, - details, - op_in_unsafe_fn_allowed, - }); - } - UnsafetyViolationKind::UnsafeFn => { - tcx.emit_node_span_lint( - UNSAFE_OP_IN_UNSAFE_FN, - lint_root, - source_info.span, - errors::UnsafeOpInUnsafeFn { - details, - suggest_unsafe_block: suggest_unsafe_block.then(|| { - let hir_id = tcx.local_def_id_to_hir_id(def_id); - let fn_sig = tcx - .hir() - .fn_sig_by_hir_id(hir_id) - .expect("this violation only occurs in fn"); - let body = tcx.hir().body_owned_by(def_id); - let body_span = tcx.hir().body(body).value.span; - let start = tcx.sess.source_map().start_point(body_span).shrink_to_hi(); - let end = tcx.sess.source_map().end_point(body_span).shrink_to_lo(); - (start, end, fn_sig.span) - }), - }, - ); - suggest_unsafe_block = false; - } - } - } - - for &(block_id, kind) in unused_unsafes.as_ref().unwrap() { - report_unused_unsafe(tcx, kind, block_id); - } -} - -fn unsafe_op_in_unsafe_fn_allowed(tcx: TyCtxt<'_>, id: HirId) -> bool { - tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, id).0 == Level::Allow -} diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index f0a13f6655593..e2a911f0dc7d9 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -168,7 +168,7 @@ impl<'tcx> MutVisitor<'tcx> for PinArgVisitor<'tcx> { Place { local: SELF_ARG, projection: self.tcx().mk_place_elems(&[ProjectionElem::Field( - FieldIdx::new(0), + FieldIdx::ZERO, self.ref_coroutine_ty, )]), }, @@ -267,7 +267,7 @@ impl<'tcx> TransformVisitor<'tcx> { Rvalue::Aggregate( Box::new(AggregateKind::Adt( option_def_id, - VariantIdx::from_usize(0), + VariantIdx::ZERO, self.tcx.mk_args(&[self.old_yield_ty.into()]), None, None, @@ -329,7 +329,7 @@ impl<'tcx> TransformVisitor<'tcx> { Rvalue::Aggregate( Box::new(AggregateKind::Adt( poll_def_id, - VariantIdx::from_usize(0), + VariantIdx::ZERO, args, None, None, @@ -358,7 +358,7 @@ impl<'tcx> TransformVisitor<'tcx> { Rvalue::Aggregate( Box::new(AggregateKind::Adt( option_def_id, - VariantIdx::from_usize(0), + VariantIdx::ZERO, args, None, None, @@ -420,7 +420,7 @@ impl<'tcx> TransformVisitor<'tcx> { Rvalue::Aggregate( Box::new(AggregateKind::Adt( coroutine_state_def_id, - VariantIdx::from_usize(0), + VariantIdx::ZERO, args, None, None, @@ -1226,7 +1226,7 @@ fn create_coroutine_drop_shim<'tcx>( tcx: TyCtxt<'tcx>, transform: &TransformVisitor<'tcx>, coroutine_ty: Ty<'tcx>, - body: &mut Body<'tcx>, + body: &Body<'tcx>, drop_clean: BasicBlock, ) -> Body<'tcx> { let mut body = body.clone(); diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs index e0bbd582d88c2..de43f9faff909 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -1,9 +1,68 @@ -//! A MIR pass which duplicates a coroutine's body and removes any derefs which -//! would be present for upvars that are taken by-ref. The result of which will -//! be a coroutine body that takes all of its upvars by-move, and which we stash -//! into the `CoroutineInfo` for all coroutines returned by coroutine-closures. +//! This pass constructs a second coroutine body sufficient for return from +//! `FnOnce`/`AsyncFnOnce` implementations for coroutine-closures (e.g. async closures). +//! +//! Consider an async closure like: +//! ```rust +//! #![feature(async_closure)] +//! +//! let x = vec![1, 2, 3]; +//! +//! let closure = async move || { +//! println!("{x:#?}"); +//! }; +//! ``` +//! +//! This desugars to something like: +//! ```rust,ignore (invalid-borrowck) +//! let x = vec![1, 2, 3]; +//! +//! let closure = move || { +//! async { +//! println!("{x:#?}"); +//! } +//! }; +//! ``` +//! +//! Important to note here is that while the outer closure *moves* `x: Vec` +//! into its upvars, the inner `async` coroutine simply captures a ref of `x`. +//! This is the "magic" of async closures -- the futures that they return are +//! allowed to borrow from their parent closure's upvars. +//! +//! However, what happens when we call `closure` with `AsyncFnOnce` (or `FnOnce`, +//! since all async closures implement that too)? Well, recall the signature: +//! ``` +//! use std::future::Future; +//! pub trait AsyncFnOnce +//! { +//! type CallOnceFuture: Future; +//! type Output; +//! fn async_call_once( +//! self, +//! args: Args +//! ) -> Self::CallOnceFuture; +//! } +//! ``` +//! +//! This signature *consumes* the async closure (`self`) and returns a `CallOnceFuture`. +//! How do we deal with the fact that the coroutine is supposed to take a reference +//! to the captured `x` from the parent closure, when that parent closure has been +//! destroyed? +//! +//! This is the second piece of magic of async closures. We can simply create a +//! *second* `async` coroutine body where that `x` that was previously captured +//! by reference is now captured by value. This means that we consume the outer +//! closure and return a new coroutine that will hold onto all of these captures, +//! and drop them when it is finished (i.e. after it has been `.await`ed). +//! +//! We do this with the analysis below, which detects the captures that come from +//! borrowing from the outer closure, and we simply peel off a `deref` projection +//! from them. This second body is stored alongside the first body, and optimized +//! with it in lockstep. When we need to resolve a body for `FnOnce` or `AsyncFnOnce`, +//! we use this "by move" body instead. -use rustc_data_structures::fx::FxIndexSet; +use itertools::Itertools; + +use rustc_data_structures::unord::UnordSet; use rustc_hir as hir; use rustc_middle::mir::visit::MutVisitor; use rustc_middle::mir::{self, dump_mir, MirPass}; @@ -14,6 +73,8 @@ pub struct ByMoveBody; impl<'tcx> MirPass<'tcx> for ByMoveBody { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) { + // We only need to generate by-move coroutine bodies for coroutines that come + // from coroutine-closures. let Some(coroutine_def_id) = body.source.def_id().as_local() else { return; }; @@ -22,44 +83,78 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody { else { return; }; + + // Also, let's skip processing any bodies with errors, since there's no guarantee + // the MIR body will be constructed well. let coroutine_ty = body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty; if coroutine_ty.references_error() { return; } - let ty::Coroutine(_, args) = *coroutine_ty.kind() else { bug!("{body:#?}") }; - let coroutine_kind = args.as_coroutine().kind_ty().to_opt_closure_kind().unwrap(); - if coroutine_kind == ty::ClosureKind::FnOnce { + // We don't need to generate a by-move coroutine if the coroutine body was + // produced by the `CoroutineKindShim`, since it's already by-move. + if matches!(body.source.instance, ty::InstanceDef::CoroutineKindShim { .. }) { return; } - let mut by_ref_fields = FxIndexSet::default(); - let by_move_upvars = Ty::new_tup_from_iter( - tcx, - tcx.closure_captures(coroutine_def_id).iter().enumerate().map(|(idx, capture)| { - if capture.is_by_ref() { - by_ref_fields.insert(FieldIdx::from_usize(idx)); - } - capture.place.ty() - }), - ); - let by_move_coroutine_ty = Ty::new_coroutine( - tcx, - coroutine_def_id.to_def_id(), - ty::CoroutineArgs::new( + let ty::Coroutine(_, args) = *coroutine_ty.kind() else { bug!("{body:#?}") }; + let args = args.as_coroutine(); + + let coroutine_kind = args.kind_ty().to_opt_closure_kind().unwrap(); + + let parent_def_id = tcx.local_parent(coroutine_def_id); + let ty::CoroutineClosure(_, parent_args) = + *tcx.type_of(parent_def_id).instantiate_identity().kind() + else { + bug!(); + }; + let parent_closure_args = parent_args.as_coroutine_closure(); + let num_args = parent_closure_args + .coroutine_closure_sig() + .skip_binder() + .tupled_inputs_ty + .tuple_fields() + .len(); + + let mut by_ref_fields = UnordSet::default(); + for (idx, (coroutine_capture, parent_capture)) in tcx + .closure_captures(coroutine_def_id) + .iter() + // By construction we capture all the args first. + .skip(num_args) + .zip_eq(tcx.closure_captures(parent_def_id)) + .enumerate() + { + // This upvar is captured by-move from the parent closure, but by-ref + // from the inner async block. That means that it's being borrowed from + // the outer closure body -- we need to change the coroutine to take the + // upvar by value. + if coroutine_capture.is_by_ref() && !parent_capture.is_by_ref() { + assert_ne!( + coroutine_kind, + ty::ClosureKind::FnOnce, + "`FnOnce` coroutine-closures return coroutines that capture from \ + their body; it will always result in a borrowck error!" + ); + by_ref_fields.insert(FieldIdx::from_usize(num_args + idx)); + } + + // Make sure we're actually talking about the same capture. + // FIXME(async_closures): We could look at the `hir::Upvar` instead? + assert_eq!(coroutine_capture.place.ty(), parent_capture.place.ty()); + } + + let by_move_coroutine_ty = tcx + .instantiate_bound_regions_with_erased(parent_closure_args.coroutine_closure_sig()) + .to_coroutine_given_kind_and_upvars( tcx, - ty::CoroutineArgsParts { - parent_args: args.as_coroutine().parent_args(), - kind_ty: Ty::from_closure_kind(tcx, ty::ClosureKind::FnOnce), - resume_ty: args.as_coroutine().resume_ty(), - yield_ty: args.as_coroutine().yield_ty(), - return_ty: args.as_coroutine().return_ty(), - witness: args.as_coroutine().witness(), - tupled_upvars_ty: by_move_upvars, - }, - ) - .args, - ); + parent_closure_args.parent_args(), + coroutine_def_id.to_def_id(), + ty::ClosureKind::FnOnce, + tcx.lifetimes.re_erased, + parent_closure_args.tupled_upvars_ty(), + parent_closure_args.coroutine_captures_by_ref_ty(), + ); let mut by_move_body = body.clone(); MakeByMoveBody { tcx, by_ref_fields, by_move_coroutine_ty }.visit_body(&mut by_move_body); @@ -73,7 +168,7 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody { struct MakeByMoveBody<'tcx> { tcx: TyCtxt<'tcx>, - by_ref_fields: FxIndexSet, + by_ref_fields: UnordSet, by_move_coroutine_ty: Ty<'tcx>, } @@ -89,11 +184,11 @@ impl<'tcx> MutVisitor<'tcx> for MakeByMoveBody<'tcx> { location: mir::Location, ) { if place.local == ty::CAPTURE_STRUCT_LOCAL - && !place.projection.is_empty() - && let mir::ProjectionElem::Field(idx, ty) = place.projection[0] + && let Some((&mir::ProjectionElem::Field(idx, ty), projection)) = + place.projection.split_first() && self.by_ref_fields.contains(&idx) { - let (begin, end) = place.projection[1..].split_first().unwrap(); + let (begin, end) = projection.split_first().unwrap(); // FIXME(async_closures): I'm actually a bit surprised to see that we always // initially deref the by-ref upvars. If this is not actually true, then we // will at least get an ICE that explains why this isn't true :^) diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index ae3b1a3d1af3b..d382d2c03c24a 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -123,8 +123,11 @@ fn create_mappings<'tcx>( let body_span = hir_info.body_span; let source_file = source_map.lookup_source_file(body_span.lo()); - use rustc_session::RemapFileNameExt; - let file_name = Symbol::intern(&source_file.name.for_codegen(tcx.sess).to_string_lossy()); + + use rustc_session::{config::RemapPathScopeComponents, RemapFileNameExt}; + let file_name = Symbol::intern( + &source_file.name.for_scope(tcx.sess, RemapPathScopeComponents::MACRO).to_string_lossy(), + ); let term_for_bcb = |bcb| { coverage_counters diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index b5dd9dcc7b467..65715253647a8 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -59,7 +59,7 @@ fn coverage_ids_info<'tcx>( _ => None, }) .max() - .unwrap_or(CounterId::START); + .unwrap_or(CounterId::ZERO); CoverageIdsInfo { max_counter_id } } diff --git a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs index 96943435bab89..318674f24e7ab 100644 --- a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs +++ b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs @@ -3,7 +3,6 @@ //! Box is not actually a pointer so it is incorrect to dereference it directly. use rustc_hir::def_id::DefId; -use rustc_index::Idx; use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::visit::MutVisitor; use rustc_middle::mir::*; @@ -32,9 +31,9 @@ pub fn build_projection<'tcx>( ptr_ty: Ty<'tcx>, ) -> [PlaceElem<'tcx>; 3] { [ - PlaceElem::Field(FieldIdx::new(0), unique_ty), - PlaceElem::Field(FieldIdx::new(0), nonnull_ty), - PlaceElem::Field(FieldIdx::new(0), ptr_ty), + PlaceElem::Field(FieldIdx::ZERO, unique_ty), + PlaceElem::Field(FieldIdx::ZERO, nonnull_ty), + PlaceElem::Field(FieldIdx::ZERO, ptr_ty), ] } @@ -91,15 +90,14 @@ pub struct ElaborateBoxDerefs; impl<'tcx> MirPass<'tcx> for ElaborateBoxDerefs { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { if let Some(def_id) = tcx.lang_items().owned_box() { - let unique_did = - tcx.adt_def(def_id).non_enum_variant().fields[FieldIdx::from_u32(0)].did; + let unique_did = tcx.adt_def(def_id).non_enum_variant().fields[FieldIdx::ZERO].did; let Some(nonnull_def) = tcx.type_of(unique_did).instantiate_identity().ty_adt_def() else { span_bug!(tcx.def_span(unique_did), "expected Box to contain Unique") }; - let nonnull_did = nonnull_def.non_enum_variant().fields[FieldIdx::from_u32(0)].did; + let nonnull_did = nonnull_def.non_enum_variant().fields[FieldIdx::ZERO].did; let patch = MirPatch::new(body); diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index 9297bc51fad99..0634e321ea303 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -1,11 +1,6 @@ -use std::borrow::Cow; - -use rustc_errors::{ - codes::*, Applicability, Diag, DiagArgValue, DiagCtxt, DiagMessage, Diagnostic, - EmissionGuarantee, Level, LintDiagnostic, -}; +use rustc_errors::{codes::*, Diag, DiagMessage, LintDiagnostic}; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; -use rustc_middle::mir::{AssertKind, UnsafetyViolationDetails}; +use rustc_middle::mir::AssertKind; use rustc_middle::ty::TyCtxt; use rustc_session::lint::{self, Lint}; use rustc_span::def_id::DefId; @@ -42,168 +37,6 @@ pub(crate) struct UnalignedPackedRef { pub span: Span, } -#[derive(LintDiagnostic)] -#[diag(mir_transform_unused_unsafe)] -pub(crate) struct UnusedUnsafe { - #[label(mir_transform_unused_unsafe)] - pub span: Span, - #[label] - pub nested_parent: Option, -} - -pub(crate) struct RequiresUnsafe { - pub span: Span, - pub details: RequiresUnsafeDetail, - pub enclosing: Option, - pub op_in_unsafe_fn_allowed: bool, -} - -// The primary message for this diagnostic should be '{$label} is unsafe and...', -// so we need to eagerly translate the label here, which isn't supported by the derive API -// We could also exhaustively list out the primary messages for all unsafe violations, -// but this would result in a lot of duplication. -impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for RequiresUnsafe { - #[track_caller] - fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> { - let mut diag = Diag::new(dcx, level, fluent::mir_transform_requires_unsafe); - diag.code(E0133); - diag.span(self.span); - diag.span_label(self.span, self.details.label()); - let desc = dcx.eagerly_translate_to_string(self.details.label(), [].into_iter()); - diag.arg("details", desc); - diag.arg("op_in_unsafe_fn_allowed", self.op_in_unsafe_fn_allowed); - self.details.add_subdiagnostics(&mut diag); - if let Some(sp) = self.enclosing { - diag.span_label(sp, fluent::mir_transform_not_inherited); - } - diag - } -} - -#[derive(Clone)] -pub(crate) struct RequiresUnsafeDetail { - pub span: Span, - pub violation: UnsafetyViolationDetails, -} - -impl RequiresUnsafeDetail { - // FIXME: make this translatable - #[allow(rustc::diagnostic_outside_of_impl)] - #[allow(rustc::untranslatable_diagnostic)] - fn add_subdiagnostics(&self, diag: &mut Diag<'_, G>) { - use UnsafetyViolationDetails::*; - match self.violation { - CallToUnsafeFunction => { - diag.note(fluent::mir_transform_call_to_unsafe_note); - } - UseOfInlineAssembly => { - diag.note(fluent::mir_transform_use_of_asm_note); - } - InitializingTypeWith => { - diag.note(fluent::mir_transform_initializing_valid_range_note); - } - CastOfPointerToInt => { - diag.note(fluent::mir_transform_const_ptr2int_note); - } - UseOfMutableStatic => { - diag.note(fluent::mir_transform_use_of_static_mut_note); - } - UseOfExternStatic => { - diag.note(fluent::mir_transform_use_of_extern_static_note); - } - DerefOfRawPointer => { - diag.note(fluent::mir_transform_deref_ptr_note); - } - AccessToUnionField => { - diag.note(fluent::mir_transform_union_access_note); - } - MutationOfLayoutConstrainedField => { - diag.note(fluent::mir_transform_mutation_layout_constrained_note); - } - BorrowOfLayoutConstrainedField => { - diag.note(fluent::mir_transform_mutation_layout_constrained_borrow_note); - } - CallToFunctionWith { ref missing, ref build_enabled } => { - diag.help(fluent::mir_transform_target_feature_call_help); - diag.arg( - "missing_target_features", - DiagArgValue::StrListSepByAnd( - missing.iter().map(|feature| Cow::from(feature.to_string())).collect(), - ), - ); - diag.arg("missing_target_features_count", missing.len()); - if !build_enabled.is_empty() { - diag.note(fluent::mir_transform_target_feature_call_note); - diag.arg( - "build_target_features", - DiagArgValue::StrListSepByAnd( - build_enabled - .iter() - .map(|feature| Cow::from(feature.to_string())) - .collect(), - ), - ); - diag.arg("build_target_features_count", build_enabled.len()); - } - } - } - } - - fn label(&self) -> DiagMessage { - use UnsafetyViolationDetails::*; - match self.violation { - CallToUnsafeFunction => fluent::mir_transform_call_to_unsafe_label, - UseOfInlineAssembly => fluent::mir_transform_use_of_asm_label, - InitializingTypeWith => fluent::mir_transform_initializing_valid_range_label, - CastOfPointerToInt => fluent::mir_transform_const_ptr2int_label, - UseOfMutableStatic => fluent::mir_transform_use_of_static_mut_label, - UseOfExternStatic => fluent::mir_transform_use_of_extern_static_label, - DerefOfRawPointer => fluent::mir_transform_deref_ptr_label, - AccessToUnionField => fluent::mir_transform_union_access_label, - MutationOfLayoutConstrainedField => { - fluent::mir_transform_mutation_layout_constrained_label - } - BorrowOfLayoutConstrainedField => { - fluent::mir_transform_mutation_layout_constrained_borrow_label - } - CallToFunctionWith { .. } => fluent::mir_transform_target_feature_call_label, - } - } -} - -pub(crate) struct UnsafeOpInUnsafeFn { - pub details: RequiresUnsafeDetail, - - /// These spans point to: - /// 1. the start of the function body - /// 2. the end of the function body - /// 3. the function signature - pub suggest_unsafe_block: Option<(Span, Span, Span)>, -} - -impl<'a> LintDiagnostic<'a, ()> for UnsafeOpInUnsafeFn { - #[track_caller] - fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { - let desc = diag.dcx.eagerly_translate_to_string(self.details.label(), [].into_iter()); - diag.arg("details", desc); - diag.span_label(self.details.span, self.details.label()); - self.details.add_subdiagnostics(diag); - - if let Some((start, end, fn_sig)) = self.suggest_unsafe_block { - diag.span_note(fn_sig, fluent::mir_transform_note); - diag.tool_only_multipart_suggestion( - fluent::mir_transform_suggestion, - vec![(start, " unsafe {".into()), (end, "}".into())], - Applicability::MaybeIncorrect, - ); - } - } - - fn msg(&self) -> DiagMessage { - fluent::mir_transform_unsafe_op_in_unsafe_fn - } -} - pub(crate) struct AssertLint

{ pub span: Span, pub assert_kind: AssertKind

, diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 59d6d89cf1fec..d4f736d2a50f6 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -355,7 +355,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } fn insert_tuple(&mut self, values: Vec) -> VnIndex { - self.insert(Value::Aggregate(AggregateTy::Tuple, VariantIdx::from_u32(0), values)) + self.insert(Value::Aggregate(AggregateTy::Tuple, VariantIdx::ZERO, values)) } #[instrument(level = "trace", skip(self), ret)] diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 5f74841151cda..60513a674af95 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -324,7 +324,7 @@ impl<'tcx> Inliner<'tcx> { // do not need to catch this here, we can wait until the inliner decides to continue // inlining a second time. InstanceDef::VTableShim(_) - | InstanceDef::ReifyShim(_) + | InstanceDef::ReifyShim(..) | InstanceDef::FnPtrShim(..) | InstanceDef::ClosureOnceShim { .. } | InstanceDef::ConstructCoroutineInClosureShim { .. } @@ -1077,7 +1077,7 @@ fn try_instance_mir<'tcx>( let fields = def.all_fields(); for field in fields { let field_ty = field.ty(tcx, args); - if field_ty.has_param() && field_ty.has_projections() { + if field_ty.has_param() && field_ty.has_aliases() { return Err("cannot build drop shim for polymorphic type"); } } diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs index f2b6dcac58632..99c7b616f1b21 100644 --- a/compiler/rustc_mir_transform/src/inline/cycle.rs +++ b/compiler/rustc_mir_transform/src/inline/cycle.rs @@ -5,6 +5,7 @@ use rustc_middle::mir::TerminatorKind; use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, GenericArgsRef, InstanceDef, TyCtxt}; use rustc_session::Limit; +use rustc_span::sym; // FIXME: check whether it is cheaper to precompute the entire call graph instead of invoking // this query ridiculously often. @@ -84,7 +85,7 @@ pub(crate) fn mir_callgraph_reachable<'tcx>( // again, a function item can end up getting inlined. Thus we'll be able to cause // a cycle that way InstanceDef::VTableShim(_) - | InstanceDef::ReifyShim(_) + | InstanceDef::ReifyShim(..) | InstanceDef::FnPtrShim(..) | InstanceDef::ClosureOnceShim { .. } | InstanceDef::ConstructCoroutineInClosureShim { .. } @@ -164,11 +165,20 @@ pub(crate) fn mir_inliner_callees<'tcx>( let mut calls = FxIndexSet::default(); for bb_data in body.basic_blocks.iter() { let terminator = bb_data.terminator(); - if let TerminatorKind::Call { func, .. } = &terminator.kind { + if let TerminatorKind::Call { func, args: call_args, .. } = &terminator.kind { let ty = func.ty(&body.local_decls, tcx); - let call = match ty.kind() { - ty::FnDef(def_id, args) => (*def_id, *args), - _ => continue, + let ty::FnDef(def_id, generic_args) = ty.kind() else { + continue; + }; + let call = if tcx.is_intrinsic(*def_id, sym::const_eval_select) { + let func = &call_args[2].node; + let ty = func.ty(&body.local_decls, tcx); + let ty::FnDef(def_id, generic_args) = ty.kind() else { + continue; + }; + (*def_id, *generic_args) + } else { + (*def_id, *generic_args) }; calls.insert(call); } diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index 6b33d81c1c412..1b38eeccfad04 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -1,10 +1,12 @@ //! Performs various peephole optimizations. use crate::simplify::simplify_duplicate_switch_targets; +use rustc_ast::attr; use rustc_middle::mir::*; use rustc_middle::ty::layout; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{self, GenericArgsRef, ParamEnv, Ty, TyCtxt}; +use rustc_span::sym; use rustc_span::symbol::Symbol; use rustc_target::abi::FieldIdx; use rustc_target::spec::abi::Abi; @@ -22,10 +24,15 @@ impl<'tcx> MirPass<'tcx> for InstSimplify { local_decls: &body.local_decls, param_env: tcx.param_env_reveal_all_normalized(body.source.def_id()), }; + let preserve_ub_checks = + attr::contains_name(tcx.hir().krate_attrs(), sym::rustc_preserve_ub_checks); for block in body.basic_blocks.as_mut() { for statement in block.statements.iter_mut() { match statement.kind { StatementKind::Assign(box (_place, ref mut rvalue)) => { + if !preserve_ub_checks { + ctx.simplify_ub_check(&statement.source_info, rvalue); + } ctx.simplify_bool_cmp(&statement.source_info, rvalue); ctx.simplify_ref_deref(&statement.source_info, rvalue); ctx.simplify_len(&statement.source_info, rvalue); @@ -140,6 +147,14 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> { } } + fn simplify_ub_check(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) { + if let Rvalue::NullaryOp(NullOp::UbChecks, _) = *rvalue { + let const_ = Const::from_bool(self.tcx, self.tcx.sess.opts.debug_assertions); + let constant = ConstOperand { span: source_info.span, const_, user_ty: None }; + *rvalue = Rvalue::Use(Operand::Constant(Box::new(constant))); + } + } + fn simplify_cast(&self, rvalue: &mut Rvalue<'tcx>) { if let Rvalue::Cast(kind, operand, cast_ty) = rvalue { let operand_ty = operand.ty(self.local_decls, self.tcx); diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index a20958e74dffe..2218154ea5e78 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_const_eval::interpret::{ use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::DefKind; use rustc_hir::HirId; -use rustc_index::{bit_set::BitSet, Idx, IndexVec}; +use rustc_index::{bit_set::BitSet, IndexVec}; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout}; @@ -124,10 +124,8 @@ impl<'tcx> Value<'tcx> { fields.ensure_contains_elem(*idx, || Value::Uninit) } (PlaceElem::Field(..), val @ Value::Uninit) => { - *val = Value::Aggregate { - variant: VariantIdx::new(0), - fields: Default::default(), - }; + *val = + Value::Aggregate { variant: VariantIdx::ZERO, fields: Default::default() }; val.project_mut(&[*proj])? } _ => return None, @@ -572,7 +570,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { self.use_ecx(|this| this.ecx.overflowing_binary_op(bin_op, &left, &right))?; let overflowed = ImmTy::from_bool(overflowed, self.tcx); Value::Aggregate { - variant: VariantIdx::new(0), + variant: VariantIdx::ZERO, fields: [Value::from(val), overflowed.into()].into_iter().collect(), } } @@ -607,7 +605,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { | AggregateKind::Tuple | AggregateKind::Closure(_, _) | AggregateKind::Coroutine(_, _) - | AggregateKind::CoroutineClosure(_, _) => VariantIdx::new(0), + | AggregateKind::CoroutineClosure(_, _) => VariantIdx::ZERO, }, } } diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 15988c0ea6b37..e477c068229ff 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -53,7 +53,6 @@ mod add_moves_for_packed_drops; mod add_retag; mod check_const_item_mutation; mod check_packed_ref; -pub mod check_unsafety; mod remove_place_mention; // This pass is public to allow external drivers to perform MIR cleanup mod add_subtyping_projections; @@ -110,7 +109,7 @@ pub mod simplify; mod simplify_branches; mod simplify_comparison_integral; mod sroa; -mod uninhabited_enum_branching; +mod unreachable_enum_branching; mod unreachable_prop; use rustc_const_eval::transform::check_consts::{self, ConstCx}; @@ -120,7 +119,6 @@ use rustc_mir_dataflow::rustc_peek; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } pub fn provide(providers: &mut Providers) { - check_unsafety::provide(providers); coverage::query::provide(providers); ffi_unwind_calls::provide(providers); shim::provide(providers); @@ -280,11 +278,6 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def: LocalDefId) -> ConstQualifs { } fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal> { - // MIR unsafety check uses the raw mir, so make sure it is run. - if !tcx.sess.opts.unstable_opts.thir_unsafeck { - tcx.ensure_with_value().mir_unsafety_check_result(def); - } - let mut body = tcx.build_mir(def); pass_manager::dump_mir_for_phase_change(tcx, &body); @@ -580,9 +573,10 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &remove_zsts::RemoveZsts, &remove_unneeded_drops::RemoveUnneededDrops, // Type instantiation may create uninhabited enums. - &uninhabited_enum_branching::UninhabitedEnumBranching, + // Also eliminates some unreachable branches based on variants of enums. + &unreachable_enum_branching::UnreachableEnumBranching, &unreachable_prop::UnreachablePropagation, - &o1(simplify::SimplifyCfg::AfterUninhabitedEnumBranching), + &o1(simplify::SimplifyCfg::AfterUnreachableEnumBranching), // Inlining may have introduced a lot of redundant code and a large move pattern. // Now, we need to shrink the generated MIR. diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index 7d4c1b9c21a62..7e8920604c176 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -90,6 +90,7 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { sym::wrapping_add | sym::wrapping_sub | sym::wrapping_mul + | sym::three_way_compare | sym::unchecked_add | sym::unchecked_sub | sym::unchecked_mul @@ -109,6 +110,7 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { sym::wrapping_add => BinOp::Add, sym::wrapping_sub => BinOp::Sub, sym::wrapping_mul => BinOp::Mul, + sym::three_way_compare => BinOp::Cmp, sym::unchecked_add => BinOp::AddUnchecked, sym::unchecked_sub => BinOp::SubUnchecked, sym::unchecked_mul => BinOp::MulUnchecked, diff --git a/compiler/rustc_mir_transform/src/nrvo.rs b/compiler/rustc_mir_transform/src/nrvo.rs index c3a92911bbf0f..232c290e0fb21 100644 --- a/compiler/rustc_mir_transform/src/nrvo.rs +++ b/compiler/rustc_mir_transform/src/nrvo.rs @@ -84,7 +84,7 @@ impl<'tcx> MirPass<'tcx> for RenameReturnPlace { /// /// If the MIR fulfills both these conditions, this function returns the `Local` that is assigned /// to the return place along all possible paths through the control-flow graph. -fn local_eligible_for_nrvo(body: &mut mir::Body<'_>) -> Option { +fn local_eligible_for_nrvo(body: &mir::Body<'_>) -> Option { if IsReturnPlaceRead::run(body) { return None; } @@ -118,10 +118,7 @@ fn local_eligible_for_nrvo(body: &mut mir::Body<'_>) -> Option { copied_to_return_place } -fn find_local_assigned_to_return_place( - start: BasicBlock, - body: &mut 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()); diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 2951897ebd697..a9d4b860b7ad8 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -434,7 +434,7 @@ impl<'tcx> Validator<'_, 'tcx> { Rvalue::ThreadLocalRef(_) => return Err(Unpromotable), // ptr-to-int casts are not possible in consts and thus not promotable - Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => return Err(Unpromotable), + Rvalue::Cast(CastKind::PointerExposeProvenance, _, _) => return Err(Unpromotable), // all other casts including int-to-ptr casts are fine, they just use the integer value // at pointer type. @@ -525,6 +525,7 @@ impl<'tcx> Validator<'_, 'tcx> { | BinOp::Lt | BinOp::Ge | BinOp::Gt + | BinOp::Cmp | BinOp::Offset | BinOp::Add | BinOp::AddUnchecked diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index b60ee7649b24a..fa6906bdd554f 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -55,7 +55,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' // a virtual call, or a direct call to a function for which // indirect calls must be codegen'd differently than direct ones // (such as `#[track_caller]`). - ty::InstanceDef::ReifyShim(def_id) => { + ty::InstanceDef::ReifyShim(def_id, _) => { build_call_shim(tcx, instance, None, CallKind::Direct(def_id)) } ty::InstanceDef::ClosureOnceShim { call_once: _, track_caller: _ } => { @@ -985,7 +985,7 @@ fn build_fn_ptr_addr_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'t let locals = local_decls_for_sig(&sig, span); let source_info = SourceInfo::outermost(span); - // FIXME: use `expose_addr` once we figure out whether function pointers have meaningful provenance. + // FIXME: use `expose_provenance` once we figure out whether function pointers have meaningful provenance. let rvalue = Rvalue::Cast( CastKind::FnPtrToPtr, Operand::Move(Place::from(Local::new(1))), diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 574330cc355c4..5bbe3bb747fd9 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -44,7 +44,7 @@ pub enum SimplifyCfg { PreOptimizations, Final, MakeShim, - AfterUninhabitedEnumBranching, + AfterUnreachableEnumBranching, } impl SimplifyCfg { @@ -57,8 +57,8 @@ impl SimplifyCfg { SimplifyCfg::PreOptimizations => "SimplifyCfg-pre-optimizations", SimplifyCfg::Final => "SimplifyCfg-final", SimplifyCfg::MakeShim => "SimplifyCfg-make_shim", - SimplifyCfg::AfterUninhabitedEnumBranching => { - "SimplifyCfg-after-uninhabited-enum-branching" + SimplifyCfg::AfterUnreachableEnumBranching => { + "SimplifyCfg-after-unreachable-enum-branching" } } } @@ -415,7 +415,7 @@ fn make_local_map( used_locals: &UsedLocals, ) -> IndexVec> { let mut map: IndexVec> = IndexVec::from_elem(None, local_decls); - let mut used = Local::new(0); + let mut used = Local::ZERO; for alive_index in local_decls.indices() { // `is_used` treats the `RETURN_PLACE` and arguments as used. diff --git a/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs b/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs similarity index 69% rename from compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs rename to compiler/rustc_mir_transform/src/unreachable_enum_branching.rs index 57fe46ad75aee..66b6235eb9380 100644 --- a/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs +++ b/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs @@ -1,4 +1,4 @@ -//! A pass that eliminates branches on uninhabited enum variants. +//! A pass that eliminates branches on uninhabited or unreachable enum variants. use crate::MirPass; use rustc_data_structures::fx::FxHashSet; @@ -11,7 +11,7 @@ use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{Ty, TyCtxt}; use rustc_target::abi::{Abi, Variants}; -pub struct UninhabitedEnumBranching; +pub struct UnreachableEnumBranching; fn get_discriminant_local(terminator: &TerminatorKind<'_>) -> Option { if let TerminatorKind::SwitchInt { discr: Operand::Move(p), .. } = terminator { @@ -71,13 +71,13 @@ fn variant_discriminants<'tcx>( } } -impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching { +impl<'tcx> MirPass<'tcx> for UnreachableEnumBranching { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { sess.mir_opt_level() > 0 } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - trace!("UninhabitedEnumBranching starting for {:?}", body.source); + trace!("UnreachableEnumBranching starting for {:?}", body.source); let mut unreachable_targets = Vec::new(); let mut patch = MirPatch::new(body); @@ -96,8 +96,10 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching { ); let mut allowed_variants = if let Ok(layout) = layout { + // Find allowed variants based on uninhabited. variant_discriminants(&layout, discriminant_ty, tcx) } else if let Some(variant_range) = discriminant_ty.variant_range(tcx) { + // If there are some generics, we can still get the allowed variants. variant_range .map(|variant| { discriminant_ty.discriminant_for_variant(tcx, variant).unwrap().val @@ -121,9 +123,26 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching { } let otherwise_is_empty_unreachable = body.basic_blocks[targets.otherwise()].is_empty_unreachable(); - // After resolving https://github.com/llvm/llvm-project/issues/78578, - // we can remove the limit on the number of successors. fn check_successors(basic_blocks: &BasicBlocks<'_>, bb: BasicBlock) -> bool { + // After resolving https://github.com/llvm/llvm-project/issues/78578, + // We can remove this check. + // The main issue here is that `early-tailduplication` causes compile time overhead + // and potential performance problems. + // Simply put, when encounter a switch (indirect branch) statement, + // `early-tailduplication` tries to duplicate the switch branch statement with BB + // into (each) predecessors. This makes CFG very complex. + // We can understand it as it transforms the following code + // ```rust + // match a { ... many cases }; + // match b { ... many cases }; + // ``` + // into + // ```rust + // match a { ... many match b { goto BB cases } } + // ... BB cases + // ``` + // Abandon this transformation when it is possible (the best effort) + // to encounter the problem. let mut successors = basic_blocks[bb].terminator().successors(); let Some(first_successor) = successors.next() else { return true }; if successors.next().is_some() { @@ -136,11 +155,32 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching { }; true } + // If and only if there is a variant that does not have a branch set, + // change the current of otherwise as the variant branch and set otherwise to unreachable. + // It transforms following code + // ```rust + // match c { + // Ordering::Less => 1, + // Ordering::Equal => 2, + // _ => 3, + // } + // ``` + // to + // ```rust + // match c { + // Ordering::Less => 1, + // Ordering::Equal => 2, + // Ordering::Greater => 3, + // } + // ``` let otherwise_is_last_variant = !otherwise_is_empty_unreachable && allowed_variants.len() == 1 - && check_successors(&body.basic_blocks, targets.otherwise()); + // Despite the LLVM issue, we hope that small enum can still be transformed. + // This is valuable for both `a <= b` and `if let Some/Ok(v)`. + && (targets.all_targets().len() <= 3 + || check_successors(&body.basic_blocks, targets.otherwise())); let replace_otherwise_to_unreachable = otherwise_is_last_variant - || !otherwise_is_empty_unreachable && allowed_variants.is_empty(); + || (!otherwise_is_empty_unreachable && allowed_variants.is_empty()); if unreachable_targets.is_empty() && !replace_otherwise_to_unreachable { continue; @@ -150,6 +190,7 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching { let mut targets = targets.clone(); if replace_otherwise_to_unreachable { if otherwise_is_last_variant { + // We have checked that `allowed_variants` has only one element. #[allow(rustc::potential_query_instability)] let last_variant = *allowed_variants.iter().next().unwrap(); targets.add_target(last_variant, targets.otherwise()); diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 3285cbd04327f..0c35f9838ed3f 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -971,16 +971,17 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { } } } - mir::TerminatorKind::Assert { ref msg, .. } => { - let lang_item = match &**msg { - mir::AssertKind::BoundsCheck { .. } => LangItem::PanicBoundsCheck, - mir::AssertKind::MisalignedPointerDereference { .. } => { - LangItem::PanicMisalignedPointerDereference - } - _ => LangItem::Panic, - }; - push_mono_lang_item(self, lang_item); - } + mir::TerminatorKind::Assert { ref msg, .. } => match &**msg { + mir::AssertKind::BoundsCheck { .. } => { + push_mono_lang_item(self, LangItem::PanicBoundsCheck); + } + mir::AssertKind::MisalignedPointerDereference { .. } => { + push_mono_lang_item(self, LangItem::PanicMisalignedPointerDereference); + } + _ => { + push_mono_lang_item(self, msg.panic_function()); + } + }, mir::TerminatorKind::UnwindTerminate(reason) => { push_mono_lang_item(self, reason.lang_item()); } diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index 9c4a6e69a3cb9..2a91d69529a85 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -14,6 +14,7 @@ use rustc_middle::ty::adjustment::CustomCoerceUnsized; use rustc_middle::ty::Instance; use rustc_middle::ty::TyCtxt; use rustc_middle::ty::{self, Ty}; +use rustc_span::def_id::DefId; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::ErrorGuaranteed; @@ -57,13 +58,24 @@ fn custom_coerce_unsize_info<'tcx>( /// linkers will optimize such that dead calls to unresolved symbols are not an error, but this is /// not guaranteed. So we used this function in codegen backends to ensure we do not generate any /// unlinkable calls. +/// +/// Note that calls to LLVM intrinsics are uniquely okay because they won't make it to the linker. pub fn is_call_from_compiler_builtins_to_upstream_monomorphization<'tcx>( tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, ) -> bool { - !instance.def_id().is_local() + fn is_llvm_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + if let Some(name) = tcx.codegen_fn_attrs(def_id).link_name { + name.as_str().starts_with("llvm.") + } else { + false + } + } + + let def_id = instance.def_id(); + !def_id.is_local() && tcx.is_compiler_builtins(LOCAL_CRATE) - && tcx.codegen_fn_attrs(instance.def_id()).link_name.is_none() + && !is_llvm_intrinsic(tcx, def_id) && !should_codegen_locally(tcx, instance) } diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index 16d8453ea24ef..1899517c0e248 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -296,10 +296,7 @@ impl, I: Interner> TypeFolder Region::new_anon_bound(self.interner(), self.binder_index, var) } - fn fold_ty(&mut self, t: I::Ty) -> I::Ty - where - I::Ty: TypeSuperFoldable, - { + fn fold_ty(&mut self, t: I::Ty) -> I::Ty { let kind = match t.kind() { ty::Infer(i) => match i { ty::TyVar(vid) => { @@ -378,47 +375,48 @@ impl, I: Interner> TypeFolder Ty::new_anon_bound(self.interner(), self.binder_index, var) } - fn fold_const(&mut self, c: I::Const) -> I::Const - where - I::Const: TypeSuperFoldable, - { + fn fold_const(&mut self, c: I::Const) -> I::Const { + // We could canonicalize all consts with static types, but the only ones we + // *really* need to worry about are the ones that we end up putting into `CanonicalVarKind` + // since canonical vars can't reference other canonical vars. + let ty = c + .ty() + .fold_with(&mut RegionsToStatic { interner: self.interner(), binder: ty::INNERMOST }); let kind = match c.kind() { - ty::ConstKind::Infer(i) => { - // FIXME: we should fold the ty too eventually - match i { - ty::InferConst::Var(vid) => { - assert_eq!( - self.infcx.root_ct_var(vid), - vid, - "region vid should have been resolved fully before canonicalization" - ); - assert_eq!( - self.infcx.probe_ct_var(vid), - None, - "region vid should have been resolved fully before canonicalization" - ); - CanonicalVarKind::Const(self.infcx.universe_of_ct(vid).unwrap(), c.ty()) - } - ty::InferConst::EffectVar(_) => CanonicalVarKind::Effect, - ty::InferConst::Fresh(_) => todo!(), + ty::ConstKind::Infer(i) => match i { + ty::InferConst::Var(vid) => { + assert_eq!( + self.infcx.root_ct_var(vid), + vid, + "region vid should have been resolved fully before canonicalization" + ); + assert_eq!( + self.infcx.probe_ct_var(vid), + None, + "region vid should have been resolved fully before canonicalization" + ); + CanonicalVarKind::Const(self.infcx.universe_of_ct(vid).unwrap(), ty) } - } + ty::InferConst::EffectVar(_) => CanonicalVarKind::Effect, + ty::InferConst::Fresh(_) => todo!(), + }, ty::ConstKind::Placeholder(placeholder) => match self.canonicalize_mode { CanonicalizeMode::Input => CanonicalVarKind::PlaceholderConst( PlaceholderLike::new(placeholder.universe(), self.variables.len().into()), - c.ty(), + ty, ), CanonicalizeMode::Response { .. } => { - CanonicalVarKind::PlaceholderConst(placeholder, c.ty()) + CanonicalVarKind::PlaceholderConst(placeholder, ty) } }, ty::ConstKind::Param(_) => match self.canonicalize_mode { CanonicalizeMode::Input => CanonicalVarKind::PlaceholderConst( PlaceholderLike::new(ty::UniverseIndex::ROOT, self.variables.len().into()), - c.ty(), + ty, ), CanonicalizeMode::Response { .. } => panic!("param ty in response: {c:?}"), }, + // FIXME: See comment above -- we could fold the region separately or something. ty::ConstKind::Bound(_, _) | ty::ConstKind::Unevaluated(_) | ty::ConstKind::Value(_) @@ -435,6 +433,35 @@ impl, I: Interner> TypeFolder }), ); - Const::new_anon_bound(self.interner(), self.binder_index, var, c.ty()) + Const::new_anon_bound(self.interner(), self.binder_index, var, ty) + } +} + +struct RegionsToStatic { + interner: I, + binder: ty::DebruijnIndex, +} + +impl TypeFolder for RegionsToStatic { + fn interner(&self) -> I { + self.interner + } + + fn fold_binder(&mut self, t: I::Binder) -> I::Binder + where + T: TypeFoldable, + I::Binder: TypeSuperFoldable, + { + self.binder.shift_in(1); + let t = t.fold_with(self); + self.binder.shift_out(1); + t + } + + fn fold_region(&mut self, r: I::Region) -> I::Region { + match r.kind() { + ty::ReBound(db, _) if self.binder > db => r, + _ => Region::new_static(self.interner()), + } } } diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 8957d7d1bd36f..e2436759c22c5 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -655,9 +655,6 @@ parse_question_mark_in_type = invalid `?` in type parse_recover_import_as_use = expected item, found {$token_name} .suggestion = items are imported using the `use` keyword -parse_ref_mut_order_incorrect = the order of `mut` and `ref` is incorrect - .suggestion = try switching the order - parse_remove_let = expected pattern, found `let` .suggestion = remove the unnecessary `let` keyword diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index a6eedabf6892c..eae2d904c35e1 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2364,14 +2364,6 @@ pub(crate) struct UnexpectedLifetimeInPattern { pub symbol: Symbol, } -#[derive(Diagnostic)] -#[diag(parse_ref_mut_order_incorrect)] -pub(crate) struct RefMutOrderIncorrect { - #[primary_span] - #[suggestion(code = "ref mut", applicability = "machine-applicable")] - pub span: Span, -} - #[derive(Diagnostic)] pub(crate) enum InvalidMutInPattern { #[diag(parse_mut_on_nested_ident_pattern)] diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 63b2b47630b29..69b48bf0aff71 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -30,7 +30,7 @@ use unescape_error_reporting::{emit_unescape_error, escaped_char}; // // This assertion is in this crate, rather than in `rustc_lexer`, because that // crate cannot depend on `rustc_data_structures`. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(rustc_lexer::Token, 12); #[derive(Clone, Debug)] diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index a1dd7d6f67345..baaed5ec37b7f 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -454,7 +454,7 @@ fn make_token_stream( } // Some types are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 18fb858c84cf6..012285e4644d6 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -860,6 +860,7 @@ impl<'a> Parser<'a> { ExprKind::MethodCall(_) => "a method call", ExprKind::Call(_, _) => "a function call", ExprKind::Await(_, _) => "`.await`", + ExprKind::Match(_, _, MatchKind::Postfix) => "a postfix match", ExprKind::Err(_) => return Ok(with_postfix), _ => unreachable!("parse_dot_or_call_expr_with_ shouldn't produce this"), } diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index de83528b52cd8..09bc00403f388 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -24,12 +24,11 @@ use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::{AttributesData, DelimSpacing, DelimSpan, Spacing}; use rustc_ast::tokenstream::{TokenStream, TokenTree, TokenTreeCursor}; use rustc_ast::util::case::Case; -use rustc_ast::AttrId; -use rustc_ast::CoroutineKind; -use rustc_ast::DUMMY_NODE_ID; -use rustc_ast::{self as ast, AnonConst, Const, DelimArgs, Extern}; -use rustc_ast::{AttrArgs, AttrArgsEq, Expr, ExprKind, Mutability, StrLit}; -use rustc_ast::{HasAttrs, HasTokens, Unsafe, Visibility, VisibilityKind}; +use rustc_ast::{ + self as ast, AnonConst, AttrArgs, AttrArgsEq, AttrId, ByRef, Const, CoroutineKind, DelimArgs, + Expr, ExprKind, Extern, HasAttrs, HasTokens, Mutability, StrLit, Unsafe, Visibility, + VisibilityKind, DUMMY_NODE_ID, +}; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; use rustc_errors::PResult; @@ -180,7 +179,7 @@ pub struct Parser<'a> { // This type is used a lot, e.g. it's cloned when matching many declarative macro rules with nonterminals. Make sure // it doesn't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(Parser<'_>, 264); /// Stores span information about a closure. @@ -1273,6 +1272,11 @@ impl<'a> Parser<'a> { if self.eat_keyword(kw::Mut) { Mutability::Mut } else { Mutability::Not } } + /// Parses reference binding mode (`ref`, `ref mut`, or nothing). + fn parse_byref(&mut self) -> ByRef { + if self.eat_keyword(kw::Ref) { ByRef::Yes(self.parse_mutability()) } else { ByRef::No } + } + /// Possibly parses mutability (`const` or `mut`). fn parse_const_or_mut(&mut self) -> Option { if self.eat_keyword(kw::Mut) { diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index fbc288595355a..59e0cd92c4cb4 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -4,11 +4,11 @@ use crate::errors::{ DotDotDotRestPattern, EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt, ExpectedCommaAfterPatternField, GenericArgsInPatRequireTurbofishSyntax, InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern, - PatternOnWrongSideOfAt, RefMutOrderIncorrect, RemoveLet, RepeatedMutInPattern, - SwitchRefBoxOrder, TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg, - TrailingVertNotAllowed, UnexpectedExpressionInPattern, UnexpectedLifetimeInPattern, - UnexpectedParenInRangePat, UnexpectedParenInRangePatSugg, - UnexpectedVertVertBeforeFunctionParam, UnexpectedVertVertInPattern, + PatternOnWrongSideOfAt, RemoveLet, RepeatedMutInPattern, SwitchRefBoxOrder, + TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg, TrailingVertNotAllowed, + UnexpectedExpressionInPattern, UnexpectedLifetimeInPattern, UnexpectedParenInRangePat, + UnexpectedParenInRangePatSugg, UnexpectedVertVertBeforeFunctionParam, + UnexpectedVertVertInPattern, }; use crate::parser::expr::could_be_unclosed_char_literal; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; @@ -476,7 +476,7 @@ impl<'a> Parser<'a> { // Parse `_` PatKind::Wild } else if self.eat_keyword(kw::Mut) { - self.parse_pat_ident_mut(syntax_loc)? + self.parse_pat_ident_mut()? } else if self.eat_keyword(kw::Ref) { if self.check_keyword(kw::Box) { // Suggest `box ref`. @@ -486,7 +486,7 @@ impl<'a> Parser<'a> { } // Parse ref ident @ pat / ref mut ident @ pat let mutbl = self.parse_mutability(); - self.parse_pat_ident(BindingAnnotation(ByRef::Yes, mutbl), syntax_loc)? + self.parse_pat_ident(BindingAnnotation(ByRef::Yes(mutbl), Mutability::Not), syntax_loc)? } else if self.eat_keyword(kw::Box) { self.parse_pat_box()? } else if self.check_inline_const(0) { @@ -746,13 +746,12 @@ impl<'a> Parser<'a> { } /// Parse a mutable binding with the `mut` token already eaten. - fn parse_pat_ident_mut(&mut self, syntax_loc: Option) -> PResult<'a, PatKind> { + fn parse_pat_ident_mut(&mut self) -> PResult<'a, PatKind> { let mut_span = self.prev_token.span; - if self.eat_keyword(kw::Ref) { - self.dcx().emit_err(RefMutOrderIncorrect { span: mut_span.to(self.prev_token.span) }); - return self.parse_pat_ident(BindingAnnotation::REF_MUT, syntax_loc); - } + self.recover_additional_muts(); + + let byref = self.parse_byref(); self.recover_additional_muts(); @@ -767,10 +766,12 @@ impl<'a> Parser<'a> { let mut pat = self.parse_pat_no_top_alt(Some(Expected::Identifier), None)?; // If we don't have `mut $ident (@ pat)?`, error. - if let PatKind::Ident(BindingAnnotation(ByRef::No, m @ Mutability::Not), ..) = &mut pat.kind + if let PatKind::Ident(BindingAnnotation(br @ ByRef::No, m @ Mutability::Not), ..) = + &mut pat.kind { // Don't recurse into the subpattern. // `mut` on the outer binding doesn't affect the inner bindings. + *br = byref; *m = Mutability::Mut; } else { // Add `mut` to any binding in the parsed pattern. @@ -778,6 +779,10 @@ impl<'a> Parser<'a> { self.ban_mut_general_pat(mut_span, &pat, changed_any_binding); } + if matches!(pat.kind, PatKind::Ident(BindingAnnotation(ByRef::Yes(_), Mutability::Mut), ..)) + { + self.psess.gated_spans.gate(sym::mut_ref, pat.span); + } Ok(pat.into_inner().kind) } @@ -1390,16 +1395,12 @@ impl<'a> Parser<'a> { // Parsing a pattern of the form `(box) (ref) (mut) fieldname`. let is_box = self.eat_keyword(kw::Box); let boxed_span = self.token.span; - let is_ref = self.eat_keyword(kw::Ref); - let is_mut = self.eat_keyword(kw::Mut); + let mutability = self.parse_mutability(); + let by_ref = self.parse_byref(); + let fieldname = self.parse_field_name()?; hi = self.prev_token.span; - - let mutability = match is_mut { - false => Mutability::Not, - true => Mutability::Mut, - }; - let ann = BindingAnnotation(ByRef::from(is_ref), mutability); + let ann = BindingAnnotation(by_ref, mutability); let fieldpat = self.mk_pat_ident(boxed_span.to(hi), ann, fieldname); let subpat = if is_box { self.mk_pat(lo.to(hi), PatKind::Box(fieldpat)) } else { fieldpat }; diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 2bb4b09e337cb..ccda43c827c57 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -1087,7 +1087,7 @@ fn unescape_string(string: &str) -> Option { } // Assert a reasonable size for `Piece` -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] rustc_index::static_assert_size!(Piece<'_>, 16); #[cfg(test)] diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs index f34e8d96f0587..438c583db49e5 100644 --- a/compiler/rustc_passes/src/entry.rs +++ b/compiler/rustc_passes/src/entry.rs @@ -7,6 +7,7 @@ use rustc_hir::{ItemId, Node, CRATE_HIR_ID}; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::config::{sigpipe, CrateType, EntryFnType}; +use rustc_session::{config::RemapPathScopeComponents, RemapFileNameExt}; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; @@ -165,10 +166,14 @@ fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) { // There is no main function. let mut has_filename = true; - let filename = tcx.sess.local_crate_source_file().unwrap_or_else(|| { - has_filename = false; - Default::default() - }); + let filename = tcx + .sess + .local_crate_source_file() + .map(|src| src.for_scope(&tcx.sess, RemapPathScopeComponents::DIAGNOSTICS).to_path_buf()) + .unwrap_or_else(|| { + has_filename = false; + Default::default() + }); let main_def_opt = tcx.resolutions(()).main_def; let code = E0601; let add_teach_note = tcx.sess.teach(code); diff --git a/compiler/rustc_pattern_analysis/Cargo.toml b/compiler/rustc_pattern_analysis/Cargo.toml index 6357d18b9da84..0cb47e03441ba 100644 --- a/compiler/rustc_pattern_analysis/Cargo.toml +++ b/compiler/rustc_pattern_analysis/Cargo.toml @@ -24,7 +24,7 @@ tracing = "0.1" [dev-dependencies] tracing-subscriber = { version = "0.3.3", default-features = false, features = ["fmt", "env-filter", "ansi"] } -tracing-tree = "0.2.0" +tracing-tree = "0.3.0" [features] default = ["rustc"] diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index 95c5556410d34..44f09b66bf6c6 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -140,6 +140,34 @@ //! [`ConstructorSet::split`]. The invariants of [`SplitConstructorSet`] are also of interest. //! //! +//! ## Unions +//! +//! Unions allow us to match a value via several overlapping representations at the same time. For +//! example, the following is exhaustive because when seeing the value as a boolean we handled all +//! possible cases (other cases such as `n == 3` would trigger UB). +//! +//! ```rust +//! # fn main() { +//! union U8AsBool { +//! n: u8, +//! b: bool, +//! } +//! let x = U8AsBool { n: 1 }; +//! unsafe { +//! match x { +//! U8AsBool { n: 2 } => {} +//! U8AsBool { b: true } => {} +//! U8AsBool { b: false } => {} +//! } +//! } +//! # } +//! ``` +//! +//! Pattern-matching has no knowledge that e.g. `false as u8 == 0`, so the values we consider in the +//! algorithm look like `U8AsBool { b: true, n: 2 }`. In other words, for the most part a union is +//! treated like a struct with the same fields. The difference lies in how we construct witnesses of +//! non-exhaustiveness. +//! //! //! ## Opaque patterns //! @@ -155,13 +183,13 @@ use std::iter::once; use smallvec::SmallVec; use rustc_apfloat::ieee::{DoubleS, IeeeFloat, SingleS}; -use rustc_index::bit_set::GrowableBitSet; +use rustc_index::bit_set::{BitSet, GrowableBitSet}; +use rustc_index::IndexVec; use self::Constructor::*; use self::MaybeInfiniteInt::*; use self::SliceKind::*; -use crate::index; use crate::PatCx; /// Whether we have seen a constructor in the column or not. @@ -920,10 +948,7 @@ pub enum ConstructorSet { Struct { empty: bool }, /// This type has the following list of constructors. If `variants` is empty and /// `non_exhaustive` is false, don't use this; use `NoConstructors` instead. - Variants { - variants: index::IdxContainer, - non_exhaustive: bool, - }, + Variants { variants: IndexVec, non_exhaustive: bool }, /// The type is `&T`. Ref, /// The type is a union. @@ -977,7 +1002,6 @@ impl ConstructorSet { /// any) are missing; 2/ split constructors to handle non-trivial intersections e.g. on ranges /// or slices. This can get subtle; see [`SplitConstructorSet`] for details of this operation /// and its invariants. - #[instrument(level = "debug", skip(self, ctors), ret)] pub fn split<'a>( &self, ctors: impl Iterator> + Clone, @@ -1025,7 +1049,7 @@ impl ConstructorSet { } } ConstructorSet::Variants { variants, non_exhaustive } => { - let mut seen_set = index::IdxSet::new_empty(variants.len()); + let mut seen_set = BitSet::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/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 1a1da5c55f608..6e8843d904977 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -25,50 +25,9 @@ rustc_fluent_macro::fluent_messages! { "../messages.ftl" } use std::fmt; -#[cfg(feature = "rustc")] -pub mod index { - // Faster version when the indices of variants are `0..variants.len()`. - pub use rustc_index::bit_set::BitSet as IdxSet; - pub use rustc_index::Idx; - pub use rustc_index::IndexVec as IdxContainer; -} -#[cfg(not(feature = "rustc"))] -pub mod index { - // Slower version when the indices of variants are something else. - pub trait Idx: Copy + PartialEq + Eq + std::hash::Hash {} - impl Idx for T {} - - #[derive(Debug)] - pub struct IdxContainer(pub rustc_hash::FxHashMap); - impl IdxContainer { - pub fn len(&self) -> usize { - self.0.len() - } - pub fn iter_enumerated(&self) -> impl Iterator { - self.0.iter().map(|(k, v)| (*k, v)) - } - } - - impl FromIterator for IdxContainer { - fn from_iter>(iter: T) -> Self { - Self(iter.into_iter().enumerate().collect()) - } - } - - #[derive(Debug)] - pub struct IdxSet(pub rustc_hash::FxHashSet); - impl IdxSet { - pub fn new_empty(_len: usize) -> Self { - Self(Default::default()) - } - pub fn contains(&self, elem: T) -> bool { - self.0.contains(&elem) - } - pub fn insert(&mut self, elem: T) { - self.0.insert(elem); - } - } -} +// Re-exports to avoid rustc_index version issues. +pub use rustc_index::Idx; +pub use rustc_index::IndexVec; #[cfg(feature = "rustc")] use rustc_middle::ty::Ty; @@ -96,7 +55,7 @@ pub trait PatCx: Sized + fmt::Debug { /// Errors that can abort analysis. type Error: fmt::Debug; /// The index of an enum variant. - type VariantIdx: Clone + index::Idx + fmt::Debug; + type VariantIdx: Clone + Idx + fmt::Debug; /// A string literal type StrLit: Clone + PartialEq + fmt::Debug; /// Extra data to store in a match arm. diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index b0f506c3651a7..467f09e4c2944 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -186,7 +186,6 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { /// Returns the types of the fields for a given constructor. The result must have a length of /// `ctor.arity()`. - #[instrument(level = "trace", skip(self))] pub(crate) fn ctor_sub_tys<'a>( &'a self, ctor: &'a Constructor<'p, 'tcx>, @@ -283,7 +282,6 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { /// Creates a set that represents all the constructors of `ty`. /// /// See [`crate::constructor`] for considerations of emptiness. - #[instrument(level = "debug", skip(self), ret)] pub fn ctors_for_ty( &self, ty: RevealedTy<'tcx>, diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index cdc03eaeb37c1..7246039174aff 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -871,12 +871,14 @@ impl PlaceInfo { where Cx: 'a, { + debug!(?self.ty); if self.private_uninhabited { // Skip the whole column return Ok((smallvec![Constructor::PrivateUninhabited], vec![])); } let ctors_for_ty = cx.ctors_for_ty(&self.ty)?; + debug!(?ctors_for_ty); // We treat match scrutinees of type `!` or `EmptyEnum` differently. let is_toplevel_exception = @@ -895,6 +897,7 @@ impl PlaceInfo { // Analyze the constructors present in this column. let mut split_set = ctors_for_ty.split(ctors); + debug!(?split_set); let all_missing = split_set.present.is_empty(); // Build the set of constructors we will specialize with. It must cover the whole type, so @@ -1254,7 +1257,7 @@ impl<'p, Cx: PatCx> Matrix<'p, Cx> { /// + true + [Second(true)] + /// + false + [_] + /// + _ + [_, _, tail @ ..] + -/// | ✓ | ? | // column validity +/// | ✓ | ? | // validity /// ``` impl<'p, Cx: PatCx> fmt::Debug for Matrix<'p, Cx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -1285,7 +1288,7 @@ impl<'p, Cx: PatCx> fmt::Debug for Matrix<'p, Cx> { write!(f, " {sep}")?; } if is_validity_row { - write!(f, " // column validity")?; + write!(f, " // validity")?; } write!(f, "\n")?; } @@ -1381,12 +1384,35 @@ impl WitnessStack { /// pats: [(false, "foo"), _, true] /// result: [Enum::Variant { a: (false, "foo"), b: _ }, true] /// ``` - fn apply_constructor(&mut self, pcx: &PlaceCtxt<'_, Cx>, ctor: &Constructor) { + fn apply_constructor( + mut self, + pcx: &PlaceCtxt<'_, Cx>, + ctor: &Constructor, + ) -> SmallVec<[Self; 1]> { let len = self.0.len(); let arity = pcx.ctor_arity(ctor); - let fields = self.0.drain((len - arity)..).rev().collect(); - let pat = WitnessPat::new(ctor.clone(), fields, pcx.ty.clone()); - self.0.push(pat); + let fields: Vec<_> = self.0.drain((len - arity)..).rev().collect(); + if matches!(ctor, Constructor::UnionField) + && fields.iter().filter(|p| !matches!(p.ctor(), Constructor::Wildcard)).count() >= 2 + { + // Convert a `Union { a: p, b: q }` witness into `Union { a: p }` and `Union { b: q }`. + // First add `Union { .. }` to `self`. + self.0.push(WitnessPat::wild_from_ctor(pcx.cx, ctor.clone(), pcx.ty.clone())); + fields + .into_iter() + .enumerate() + .filter(|(_, p)| !matches!(p.ctor(), Constructor::Wildcard)) + .map(|(i, p)| { + let mut ret = self.clone(); + // Fill the `i`th field of the union with `p`. + ret.0.last_mut().unwrap().fields[i] = p; + ret + }) + .collect() + } else { + self.0.push(WitnessPat::new(ctor.clone(), fields, pcx.ty.clone())); + smallvec![self] + } } } @@ -1459,8 +1485,8 @@ impl WitnessMatrix { *self = ret; } else { // Any other constructor we unspecialize as expected. - for witness in self.0.iter_mut() { - witness.apply_constructor(pcx, ctor) + for witness in std::mem::take(&mut self.0) { + self.0.extend(witness.apply_constructor(pcx, ctor)); } } } @@ -1617,7 +1643,6 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: PatCx>( }; // Analyze the constructors present in this column. - debug!("ty: {:?}", place.ty); let ctors = matrix.heads().map(|p| p.ctor()); let (split_ctors, missing_ctors) = place.split_column_ctors(mcx.tycx, ctors)?; @@ -1669,7 +1694,10 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: PatCx>( for row in matrix.rows() { if row.useful { if let PatOrWild::Pat(pat) = row.head() { - mcx.useful_subpatterns.insert(pat.uid); + let newly_useful = mcx.useful_subpatterns.insert(pat.uid); + if newly_useful { + debug!("newly useful: {pat:?}"); + } } } } @@ -1768,6 +1796,7 @@ pub fn compute_match_usefulness<'p, Cx: PatCx>( .map(|arm| { debug!(?arm); let usefulness = collect_pattern_usefulness(&cx.useful_subpatterns, arm.pat); + debug!(?usefulness); (arm, usefulness) }) .collect(); diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 9f067273f3580..534937003ebfd 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -13,6 +13,7 @@ use std::fmt::Debug; use std::hash::Hash; use std::marker::PhantomData; use std::sync::atomic::Ordering; +use std::sync::Arc; use super::query::DepGraphQuery; use super::serialized::{GraphEncoder, SerializedDepGraph, SerializedDepNodeIndex}; @@ -40,7 +41,7 @@ rustc_index::newtype_index! { } impl DepNodeIndex { - const SINGLETON_DEPENDENCYLESS_ANON_NODE: DepNodeIndex = DepNodeIndex::from_u32(0); + const SINGLETON_DEPENDENCYLESS_ANON_NODE: DepNodeIndex = DepNodeIndex::ZERO; pub const FOREVER_RED_NODE: DepNodeIndex = DepNodeIndex::from_u32(1); } @@ -81,7 +82,7 @@ pub(crate) struct DepGraphData { /// The dep-graph from the previous compilation session. It contains all /// nodes and edges as well as all fingerprints of nodes that have them. - previous: SerializedDepGraph, + previous: Arc, colors: DepNodeColorMap, @@ -113,7 +114,7 @@ where impl DepGraph { pub fn new( profiler: &SelfProfilerRef, - prev_graph: SerializedDepGraph, + prev_graph: Arc, prev_work_products: WorkProductMap, encoder: FileEncoder, record_graph: bool, @@ -127,6 +128,7 @@ impl DepGraph { encoder, record_graph, record_stats, + prev_graph.clone(), ); let colors = DepNodeColorMap::new(prev_graph_node_count); @@ -1084,6 +1086,7 @@ impl CurrentDepGraph { encoder: FileEncoder, record_graph: bool, record_stats: bool, + previous: Arc, ) -> Self { use std::time::{SystemTime, UNIX_EPOCH}; @@ -1116,6 +1119,7 @@ impl CurrentDepGraph { record_graph, record_stats, profiler, + previous, ), new_node_to_index: Sharded::new(|| { FxHashMap::with_capacity_and_hasher( @@ -1236,16 +1240,14 @@ impl CurrentDepGraph { match prev_index_to_index[prev_index] { Some(dep_node_index) => dep_node_index, None => { - let key = prev_graph.index_to_node(prev_index); - let edges = prev_graph - .edge_targets_from(prev_index) - .map(|i| prev_index_to_index[i].unwrap()) - .collect(); - let fingerprint = prev_graph.fingerprint_by_index(prev_index); - let dep_node_index = self.encoder.send(key, fingerprint, edges); + let dep_node_index = self.encoder.send_promoted(prev_index, &*prev_index_to_index); prev_index_to_index[prev_index] = Some(dep_node_index); #[cfg(debug_assertions)] - self.record_edge(dep_node_index, key, fingerprint); + self.record_edge( + dep_node_index, + prev_graph.index_to_node(prev_index), + prev_graph.fingerprint_by_index(prev_index), + ); dep_node_index } } diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs index 0c6a63582931e..2bc7cb9954756 100644 --- a/compiler/rustc_query_system/src/dep_graph/serialized.rs +++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs @@ -41,6 +41,7 @@ use crate::dep_graph::edges::EdgesVec; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fingerprint::PackedFingerprint; use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::outline; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::sync::Lock; use rustc_data_structures::unhash::UnhashMap; @@ -49,6 +50,7 @@ use rustc_serialize::opaque::{FileEncodeResult, FileEncoder, IntEncodedWithFixed use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use std::iter; use std::marker::PhantomData; +use std::sync::Arc; // The maximum value of `SerializedDepNodeIndex` leaves the upper two bits // unused so that we can store multiple index types in `CompressedHybridIndex`, @@ -94,7 +96,7 @@ impl SerializedDepGraph { pub fn edge_targets_from( &self, source: SerializedDepNodeIndex, - ) -> impl Iterator + '_ { + ) -> impl Iterator + Clone + '_ { let header = self.edge_list_indices[source]; let mut raw = &self.edge_list_data[header.start()..]; // Figure out where the edge list for `source` ends by getting the start index of the next @@ -176,7 +178,7 @@ fn mask(bits: usize) -> usize { impl SerializedDepGraph { #[instrument(level = "debug", skip(d))] - pub fn decode(d: &mut MemDecoder<'_>) -> SerializedDepGraph { + pub fn decode(d: &mut MemDecoder<'_>) -> Arc { // The last 16 bytes are the node count and edge count. debug!("position: {:?}", d.position()); let (node_count, edge_count, graph_size) = @@ -254,7 +256,13 @@ impl SerializedDepGraph { index[node.kind.as_usize()].insert(node.hash, idx); } - SerializedDepGraph { nodes, fingerprints, edge_list_indices, edge_list_data, index } + Arc::new(SerializedDepGraph { + nodes, + fingerprints, + edge_list_indices, + edge_list_data, + index, + }) } } @@ -299,21 +307,24 @@ impl SerializedNodeHeader { const MAX_INLINE_LEN: usize = (u16::MAX as usize >> (Self::TOTAL_BITS - Self::LEN_BITS)) - 1; #[inline] - fn new(node_info: &NodeInfo) -> Self { + fn new( + node: DepNode, + fingerprint: Fingerprint, + edge_max_index: u32, + edge_count: usize, + ) -> Self { debug_assert_eq!(Self::TOTAL_BITS, Self::LEN_BITS + Self::WIDTH_BITS + Self::KIND_BITS); - let NodeInfo { node, fingerprint, edges } = node_info; - let mut head = node.kind.as_inner(); - let free_bytes = edges.max_index().leading_zeros() as usize / 8; + let free_bytes = edge_max_index.leading_zeros() as usize / 8; let bytes_per_index = (DEP_NODE_SIZE - free_bytes).saturating_sub(1); head |= (bytes_per_index as u16) << Self::KIND_BITS; // Encode number of edges + 1 so that we can reserve 0 to indicate that the len doesn't fit // in this bitfield. - if edges.len() <= Self::MAX_INLINE_LEN { - head |= (edges.len() as u16 + 1) << (Self::KIND_BITS + Self::WIDTH_BITS); + if edge_count <= Self::MAX_INLINE_LEN { + head |= (edge_count as u16 + 1) << (Self::KIND_BITS + Self::WIDTH_BITS); } let hash: Fingerprint = node.hash.into(); @@ -327,10 +338,10 @@ impl SerializedNodeHeader { #[cfg(debug_assertions)] { let res = Self { bytes, _marker: PhantomData }; - assert_eq!(node_info.fingerprint, res.fingerprint()); - assert_eq!(node_info.node, res.node()); + assert_eq!(fingerprint, res.fingerprint()); + assert_eq!(node, res.node()); if let Some(len) = res.len() { - assert_eq!(node_info.edges.len(), len); + assert_eq!(edge_count, len); } } Self { bytes, _marker: PhantomData } @@ -393,21 +404,61 @@ struct NodeInfo { impl NodeInfo { fn encode(&self, e: &mut FileEncoder) { - let header = SerializedNodeHeader::::new(self); + let NodeInfo { node, fingerprint, ref edges } = *self; + let header = + SerializedNodeHeader::::new(node, fingerprint, edges.max_index(), edges.len()); e.write_array(header.bytes); if header.len().is_none() { - e.emit_usize(self.edges.len()); + e.emit_usize(edges.len()); } let bytes_per_index = header.bytes_per_index(); - for node_index in self.edges.iter() { + for node_index in edges.iter() { e.write_with(|dest| { *dest = node_index.as_u32().to_le_bytes(); bytes_per_index }); } } + + /// Encode a node that was promoted from the previous graph. It reads the edges directly from + /// the previous dep graph and expects all edges to already have a new dep node index assigned. + /// This avoids the overhead of constructing `EdgesVec`, which would be needed to call `encode`. + #[inline] + fn encode_promoted( + e: &mut FileEncoder, + node: DepNode, + fingerprint: Fingerprint, + prev_index: SerializedDepNodeIndex, + prev_index_to_index: &IndexVec>, + previous: &SerializedDepGraph, + ) -> usize { + let edges = previous.edge_targets_from(prev_index); + let edge_count = edges.size_hint().0; + + // Find the highest edge in the new dep node indices + let edge_max = + edges.clone().map(|i| prev_index_to_index[i].unwrap().as_u32()).max().unwrap_or(0); + + let header = SerializedNodeHeader::::new(node, fingerprint, edge_max, edge_count); + e.write_array(header.bytes); + + if header.len().is_none() { + e.emit_usize(edge_count); + } + + let bytes_per_index = header.bytes_per_index(); + for node_index in edges { + let node_index = prev_index_to_index[node_index].unwrap(); + e.write_with(|dest| { + *dest = node_index.as_u32().to_le_bytes(); + bytes_per_index + }); + } + + edge_count + } } struct Stat { @@ -417,6 +468,7 @@ struct Stat { } struct EncoderState { + previous: Arc, encoder: FileEncoder, total_node_count: usize, total_edge_count: usize, @@ -428,8 +480,9 @@ struct EncoderState { } impl EncoderState { - fn new(encoder: FileEncoder, record_stats: bool) -> Self { + fn new(encoder: FileEncoder, record_stats: bool, previous: Arc) -> Self { Self { + previous, encoder, total_edge_count: 0, total_node_count: 0, @@ -439,38 +492,101 @@ impl EncoderState { } } - fn encode_node( + #[inline] + fn record( &mut self, - node: &NodeInfo, + node: DepNode, + edge_count: usize, + edges: impl FnOnce(&mut Self) -> Vec, record_graph: &Option>, ) -> DepNodeIndex { let index = DepNodeIndex::new(self.total_node_count); - self.total_node_count += 1; - self.kind_stats[node.node.kind.as_usize()] += 1; - let edge_count = node.edges.len(); + self.total_node_count += 1; + self.kind_stats[node.kind.as_usize()] += 1; self.total_edge_count += edge_count; if let Some(record_graph) = &record_graph { - // Do not ICE when a query is called from within `with_query`. - if let Some(record_graph) = &mut record_graph.try_lock() { - record_graph.push(index, node.node, &node.edges); - } + // Call `edges` before the outlined code to allow the closure to be optimized out. + let edges = edges(self); + + // Outline the build of the full dep graph as it's typically disabled and cold. + outline(move || { + // Do not ICE when a query is called from within `with_query`. + if let Some(record_graph) = &mut record_graph.try_lock() { + record_graph.push(index, node, &edges); + } + }); } if let Some(stats) = &mut self.stats { - let kind = node.node.kind; - - let stat = stats.entry(kind).or_insert(Stat { kind, node_counter: 0, edge_counter: 0 }); - stat.node_counter += 1; - stat.edge_counter += edge_count as u64; + let kind = node.kind; + + // Outline the stats code as it's typically disabled and cold. + outline(move || { + let stat = + stats.entry(kind).or_insert(Stat { kind, node_counter: 0, edge_counter: 0 }); + stat.node_counter += 1; + stat.edge_counter += edge_count as u64; + }); } - let encoder = &mut self.encoder; - node.encode::(encoder); index } + /// Encodes a node to the current graph. + fn encode_node( + &mut self, + node: &NodeInfo, + record_graph: &Option>, + ) -> DepNodeIndex { + node.encode::(&mut self.encoder); + self.record( + node.node, + node.edges.len(), + |_| node.edges[..].iter().copied().collect(), + record_graph, + ) + } + + /// Encodes a node that was promoted from the previous graph. It reads the information directly from + /// the previous dep graph for performance reasons. + /// + /// This differs from `encode_node` where you have to explictly provide the relevant `NodeInfo`. + /// + /// It expects all edges to already have a new dep node index assigned. + #[inline] + fn encode_promoted_node( + &mut self, + prev_index: SerializedDepNodeIndex, + record_graph: &Option>, + prev_index_to_index: &IndexVec>, + ) -> DepNodeIndex { + let node = self.previous.index_to_node(prev_index); + + let fingerprint = self.previous.fingerprint_by_index(prev_index); + let edge_count = NodeInfo::encode_promoted::( + &mut self.encoder, + node, + fingerprint, + prev_index, + prev_index_to_index, + &self.previous, + ); + + self.record( + node, + edge_count, + |this| { + this.previous + .edge_targets_from(prev_index) + .map(|i| prev_index_to_index[i].unwrap()) + .collect() + }, + record_graph, + ) + } + fn finish(self, profiler: &SelfProfilerRef) -> FileEncodeResult { let Self { mut encoder, @@ -479,6 +595,7 @@ impl EncoderState { stats: _, kind_stats, marker: _, + previous: _, } = self; let node_count = total_node_count.try_into().unwrap(); @@ -520,9 +637,10 @@ impl GraphEncoder { record_graph: bool, record_stats: bool, profiler: &SelfProfilerRef, + previous: Arc, ) -> Self { let record_graph = record_graph.then(|| Lock::new(DepGraphQuery::new(prev_node_count))); - let status = Lock::new(Some(EncoderState::new(encoder, record_stats))); + let status = Lock::new(Some(EncoderState::new(encoder, record_stats, previous))); GraphEncoder { status, record_graph, profiler: profiler.clone() } } @@ -596,6 +714,22 @@ impl GraphEncoder { self.status.lock().as_mut().unwrap().encode_node(&node, &self.record_graph) } + /// Encodes a node that was promoted from the previous graph. It reads the information directly from + /// the previous dep graph and expects all edges to already have a new dep node index assigned. + #[inline] + pub(crate) fn send_promoted( + &self, + prev_index: SerializedDepNodeIndex, + prev_index_to_index: &IndexVec>, + ) -> DepNodeIndex { + let _prof_timer = self.profiler.generic_activity("incr_comp_encode_dep_graph"); + self.status.lock().as_mut().unwrap().encode_promoted_node( + prev_index, + &self.record_graph, + prev_index_to_index, + ) + } + pub fn finish(&self) -> FileEncodeResult { let _prof_timer = self.profiler.generic_activity("incr_comp_encode_dep_graph_finish"); diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index b24ed573ff97d..43a43e01a9adf 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -605,6 +605,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { && !this.tcx.features().f16 && !ident.span.allows_unstable(sym::f16) && finalize.is_some() + && innermost_result.is_none() { feature_err( this.tcx.sess, @@ -618,6 +619,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { && !this.tcx.features().f128 && !ident.span.allows_unstable(sym::f128) && finalize.is_some() + && innermost_result.is_none() { feature_err( this.tcx.sess, diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 48711f435186d..76fe36a77cb8a 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -532,7 +532,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let mut seen_spans = FxHashSet::default(); let mut errors = vec![]; - let mut prev_root_id: NodeId = NodeId::from_u32(0); + let mut prev_root_id: NodeId = NodeId::ZERO; let determined_imports = mem::take(&mut self.determined_imports); let indeterminate_imports = mem::take(&mut self.indeterminate_imports); @@ -556,8 +556,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } - if prev_root_id.as_u32() != 0 - && prev_root_id.as_u32() != import.root_id.as_u32() + if prev_root_id != NodeId::ZERO + && prev_root_id != import.root_id && !errors.is_empty() { // In the case of a new import line, throw a diagnostic message diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl index 179fd79bef7c8..b8dacc6968d30 100644 --- a/compiler/rustc_session/messages.ftl +++ b/compiler/rustc_session/messages.ftl @@ -96,6 +96,8 @@ session_sanitizer_cfi_requires_lto = `-Zsanitizer=cfi` requires `-Clto` or `-Cli session_sanitizer_cfi_requires_single_codegen_unit = `-Zsanitizer=cfi` with `-Clto` requires `-Ccodegen-units=1` +session_sanitizer_kcfi_requires_panic_abort = `-Z sanitizer=kcfi` requires `-C panic=abort` + session_sanitizer_not_supported = {$us} sanitizer is not supported for this target session_sanitizers_not_supported = {$us} sanitizers are not supported for this target diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index f612e8b5b1a55..d51fcf693ed38 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -990,22 +990,12 @@ bitflags::bitflags! { const MACRO = 1 << 0; /// Apply remappings to printed compiler diagnostics const DIAGNOSTICS = 1 << 1; - /// Apply remappings to debug information only when they are written to - /// compiled executables or libraries, but not when they are in split - /// debuginfo files - const UNSPLIT_DEBUGINFO = 1 << 2; - /// Apply remappings to debug information only when they are written to - /// split debug information files, but not in compiled executables or - /// libraries - const SPLIT_DEBUGINFO = 1 << 3; - /// Apply remappings to the paths pointing to split debug information - /// files. Does nothing when these files are not generated. - const SPLIT_DEBUGINFO_PATH = 1 << 4; + /// Apply remappings to debug informations + const DEBUGINFO = 1 << 3; - /// An alias for macro,unsplit-debuginfo,split-debuginfo-path. This - /// ensures all paths in compiled executables or libraries are remapped - /// but not elsewhere. - const OBJECT = Self::MACRO.bits() | Self::UNSPLIT_DEBUGINFO.bits() | Self::SPLIT_DEBUGINFO_PATH.bits(); + /// An alias for `macro` and `debuginfo`. This ensures all paths in compiled + /// executables or libraries are remapped but not elsewhere. + const OBJECT = Self::MACRO.bits() | Self::DEBUGINFO.bits(); } } @@ -2010,7 +2000,7 @@ pub fn parse_crate_edition(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches } fn check_error_format_stability( - early_dcx: &mut EarlyDiagCtxt, + early_dcx: &EarlyDiagCtxt, unstable_opts: &UnstableOptions, error_format: ErrorOutputType, ) { @@ -2108,7 +2098,7 @@ fn should_override_cgus_and_disable_thinlto( fn collect_print_requests( early_dcx: &EarlyDiagCtxt, cg: &mut CodegenOptions, - unstable_opts: &mut UnstableOptions, + unstable_opts: &UnstableOptions, matches: &getopts::Matches, ) -> Vec { let mut prints = Vec::::new(); @@ -2574,7 +2564,7 @@ fn parse_remap_path_prefix( } fn parse_logical_env( - early_dcx: &mut EarlyDiagCtxt, + early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches, ) -> FxIndexMap { let mut vars = FxIndexMap::default(); @@ -2732,6 +2722,8 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M } if let Ok(graphviz_font) = std::env::var("RUSTC_GRAPHVIZ_FONT") { + // FIXME: this is only mutation of UnstableOptions here, move into + // UnstableOptions::build? unstable_opts.graphviz_font = graphviz_font; } @@ -2781,7 +2773,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M )); } - let prints = collect_print_requests(early_dcx, &mut cg, &mut unstable_opts, matches); + let prints = collect_print_requests(early_dcx, &mut cg, &unstable_opts, matches); let cg = cg; @@ -2852,13 +2844,8 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M early_dcx.early_fatal(format!("Current directory is invalid: {e}")); }); - let remap = file_path_mapping(remap_path_prefix.clone(), &unstable_opts); - let (path, remapped) = remap.map_prefix(&working_dir); - let working_dir = if remapped { - RealFileName::Remapped { virtual_name: path.into_owned(), local_path: Some(working_dir) } - } else { - RealFileName::LocalPath(path.into_owned()) - }; + let file_mapping = file_path_mapping(remap_path_prefix.clone(), &unstable_opts); + let working_dir = file_mapping.to_real_filename(&working_dir); let verbose = matches.opt_present("verbose") || unstable_opts.verbose_internals; diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 0a855f87586dc..2e4c7d14ecdf4 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -145,6 +145,10 @@ pub(crate) struct SanitizerCfiGeneralizePointersRequiresCfi; #[diag(session_sanitizer_cfi_normalize_integers_requires_cfi)] pub(crate) struct SanitizerCfiNormalizeIntegersRequiresCfi; +#[derive(Diagnostic)] +#[diag(session_sanitizer_kcfi_requires_panic_abort)] +pub(crate) struct SanitizerKcfiRequiresPanicAbort; + #[derive(Diagnostic)] #[diag(session_split_lto_unit_requires_lto)] pub(crate) struct SplitLtoUnitRequiresLto; diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index b0bd5e757352d..a76eb6b06aa78 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -433,7 +433,8 @@ mod desc { "a `,` separated combination of `bti`, `b-key`, `pac-ret`, or `leaf`"; pub const parse_proc_macro_execution_strategy: &str = "one of supported execution strategies (`same-thread`, or `cross-thread`)"; - pub const parse_remap_path_scope: &str = "comma separated list of scopes: `macro`, `diagnostics`, `unsplit-debuginfo`, `split-debuginfo`, `split-debuginfo-path`, `object`, `all`"; + pub const parse_remap_path_scope: &str = + "comma separated list of scopes: `macro`, `diagnostics`, `debuginfo`, `object`, `all`"; pub const parse_inlining_threshold: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or a non-negative number"; pub const parse_llvm_module_flag: &str = ":::. Type must currently be `u32`. Behavior should be one of (`error`, `warning`, `require`, `override`, `append`, `appendunique`, `max`, `min`)"; @@ -1156,9 +1157,7 @@ mod parse { *slot |= match s { "macro" => RemapPathScopeComponents::MACRO, "diagnostics" => RemapPathScopeComponents::DIAGNOSTICS, - "unsplit-debuginfo" => RemapPathScopeComponents::UNSPLIT_DEBUGINFO, - "split-debuginfo" => RemapPathScopeComponents::SPLIT_DEBUGINFO, - "split-debuginfo-path" => RemapPathScopeComponents::SPLIT_DEBUGINFO_PATH, + "debuginfo" => RemapPathScopeComponents::DEBUGINFO, "object" => RemapPathScopeComponents::OBJECT, "all" => RemapPathScopeComponents::all(), _ => return false, @@ -1951,8 +1950,6 @@ written to standard error output)"), #[rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field")] thinlto: Option = (None, parse_opt_bool, [TRACKED], "enable ThinLTO when possible"), - thir_unsafeck: bool = (true, parse_bool, [TRACKED], - "use the THIR unsafety checker (default: yes)"), /// We default to 1 here since we want to behave like /// a sequential compiler for now. This'll likely be adjusted /// in the future. Note that -Zthreads=0 is the way to get diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index e6d82d6fab352..55fff4421ae46 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -29,6 +29,7 @@ use rustc_macros::HashStable_Generic; pub use rustc_span::def_id::StableCrateId; use rustc_span::edition::Edition; use rustc_span::source_map::{FileLoader, FilePathMapping, RealFileLoader, SourceMap}; +use rustc_span::{FileNameDisplayPreference, RealFileName}; use rustc_span::{SourceFileHashAlgorithm, Span, Symbol}; use rustc_target::asm::InlineAsmArch; use rustc_target::spec::{CodeModel, PanicStrategy, RelocModel, RelroLevel}; @@ -250,13 +251,8 @@ impl Session { self.miri_unleashed_features.lock().push((span, feature_gate)); } - pub fn local_crate_source_file(&self) -> Option { - let path = self.io.input.opt_path()?; - if self.should_prefer_remapped_for_codegen() { - Some(self.opts.file_path_mapping().map_prefix(path).0.into_owned()) - } else { - Some(path.to_path_buf()) - } + pub fn local_crate_source_file(&self) -> Option { + Some(self.source_map().path_mapping().to_real_filename(self.io.input.opt_path()?)) } fn check_miri_unleashed_features(&self) -> Option { @@ -886,38 +882,19 @@ impl Session { self.opts.cg.link_dead_code.unwrap_or(false) } - pub fn should_prefer_remapped_for_codegen(&self) -> bool { - let has_split_debuginfo = match self.split_debuginfo() { - SplitDebuginfo::Off => false, - SplitDebuginfo::Packed => true, - SplitDebuginfo::Unpacked => true, - }; - - let remap_path_scopes = &self.opts.unstable_opts.remap_path_scope; - let mut prefer_remapped = false; - - if remap_path_scopes.contains(RemapPathScopeComponents::UNSPLIT_DEBUGINFO) { - prefer_remapped |= !has_split_debuginfo; - } - - if remap_path_scopes.contains(RemapPathScopeComponents::SPLIT_DEBUGINFO) { - prefer_remapped |= has_split_debuginfo; + pub fn filename_display_preference( + &self, + scope: RemapPathScopeComponents, + ) -> FileNameDisplayPreference { + assert!( + scope.bits().count_ones() == 1, + "one and only one scope should be passed to `Session::filename_display_preference`" + ); + if self.opts.unstable_opts.remap_path_scope.contains(scope) { + FileNameDisplayPreference::Remapped + } else { + FileNameDisplayPreference::Local } - - prefer_remapped - } - - pub fn should_prefer_remapped_for_split_debuginfo_paths(&self) -> bool { - let has_split_debuginfo = match self.split_debuginfo() { - SplitDebuginfo::Off => false, - SplitDebuginfo::Packed | SplitDebuginfo::Unpacked => true, - }; - - self.opts - .unstable_opts - .remap_path_scope - .contains(RemapPathScopeComponents::SPLIT_DEBUGINFO_PATH) - && has_split_debuginfo } } @@ -1234,6 +1211,11 @@ fn validate_commandline_args_with_session_available(sess: &Session) { sess.dcx().emit_err(errors::SanitizerCfiRequiresLto); } + // KCFI requires panic=abort + if sess.is_sanitizer_kcfi_enabled() && sess.panic_strategy() != PanicStrategy::Abort { + sess.dcx().emit_err(errors::SanitizerKcfiRequiresPanicAbort); + } + // LLVM CFI using rustc LTO requires a single codegen unit. if sess.is_sanitizer_cfi_enabled() && sess.lto() == config::Lto::Fat @@ -1469,12 +1451,8 @@ pub trait RemapFileNameExt { /// Returns a possibly remapped filename based on the passed scope and remap cli options. /// - /// One and only one scope should be passed to this method. For anything related to - /// "codegen" see the [`RemapFileNameExt::for_codegen`] method. + /// One and only one scope should be passed to this method, it will panic otherwise. fn for_scope(&self, sess: &Session, scope: RemapPathScopeComponents) -> Self::Output<'_>; - - /// Return a possibly remapped filename, to be used in "codegen" related parts. - fn for_codegen(&self, sess: &Session) -> Self::Output<'_>; } impl RemapFileNameExt for rustc_span::FileName { @@ -1491,14 +1469,6 @@ impl RemapFileNameExt for rustc_span::FileName { self.prefer_local() } } - - fn for_codegen(&self, sess: &Session) -> Self::Output<'_> { - if sess.should_prefer_remapped_for_codegen() { - self.prefer_remapped_unconditionaly() - } else { - self.prefer_local() - } - } } impl RemapFileNameExt for rustc_span::RealFileName { @@ -1515,12 +1485,4 @@ impl RemapFileNameExt for rustc_span::RealFileName { self.local_path_if_available() } } - - fn for_codegen(&self, sess: &Session) -> Self::Output<'_> { - if sess.should_prefer_remapped_for_codegen() { - self.remapped_path_if_available() - } else { - self.local_path_if_available() - } - } } diff --git a/compiler/rustc_session/src/utils.rs b/compiler/rustc_session/src/utils.rs index 50ebbdccf6722..58de5cb31a513 100644 --- a/compiler/rustc_session/src/utils.rs +++ b/compiler/rustc_session/src/utils.rs @@ -127,7 +127,7 @@ pub fn extra_compiler_flags() -> Option<(Vec, bool)> { const ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE: &[&str] = &["incremental"]; - let mut args = std::env::args_os().map(|arg| arg.to_string_lossy().to_string()).peekable(); + let mut args = std::env::args_os().map(|arg| arg.to_string_lossy().to_string()); let mut result = Vec::new(); let mut excluded_cargo_defaults = false; diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index d39a3788d4cf7..7c12168b80951 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -420,7 +420,10 @@ impl<'tcx> Context for TablesWrapper<'tcx> { let tcx = tables.tcx; let args = args.internal(&mut *tables, tcx); let def_ty = tables.tcx.type_of(item.internal(&mut *tables, tcx)); - def_ty.instantiate(tables.tcx, args).stable(&mut *tables) + tables + .tcx + .instantiate_and_normalize_erasing_regions(args, ty::ParamEnv::reveal_all(), def_ty) + .stable(&mut *tables) } fn const_pretty(&self, cnst: &stable_mir::ty::Const) -> String { diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index b6a722da602e0..c9f6661259022 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -267,8 +267,8 @@ impl<'tcx> Stable<'tcx> for mir::CastKind { fn stable(&self, tables: &mut Tables<'_>) -> Self::T { use rustc_middle::mir::CastKind::*; match self { - PointerExposeAddress => stable_mir::mir::CastKind::PointerExposeAddress, - PointerFromExposedAddress => stable_mir::mir::CastKind::PointerFromExposedAddress, + PointerExposeProvenance => stable_mir::mir::CastKind::PointerExposeAddress, + PointerWithExposedProvenance => stable_mir::mir::CastKind::PointerWithExposedProvenance, PointerCoercion(c) => stable_mir::mir::CastKind::PointerCoercion(c.stable(tables)), DynStar => stable_mir::mir::CastKind::DynStar, IntToInt => stable_mir::mir::CastKind::IntToInt, @@ -493,6 +493,7 @@ impl<'tcx> Stable<'tcx> for mir::BinOp { BinOp::Ne => stable_mir::mir::BinOp::Ne, BinOp::Ge => stable_mir::mir::BinOp::Ge, BinOp::Gt => stable_mir::mir::BinOp::Gt, + BinOp::Cmp => stable_mir::mir::BinOp::Cmp, BinOp::Offset => stable_mir::mir::BinOp::Offset, } } diff --git a/compiler/rustc_span/src/def_id.rs b/compiler/rustc_span/src/def_id.rs index 8f721bac95140..8925b7a42d485 100644 --- a/compiler/rustc_span/src/def_id.rs +++ b/compiler/rustc_span/src/def_id.rs @@ -22,7 +22,7 @@ rustc_index::newtype_index! { /// Item definitions in the currently-compiled crate would have the `CrateNum` /// `LOCAL_CRATE` in their `DefId`. -pub const LOCAL_CRATE: CrateNum = CrateNum::from_u32(0); +pub const LOCAL_CRATE: CrateNum = CrateNum::ZERO; impl CrateNum { #[inline] diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 37fea6c122c7a..1df2b357ac128 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -165,7 +165,7 @@ pub enum Transparency { impl LocalExpnId { /// The ID of the theoretical expansion that generates freshly parsed, unexpanded AST. - pub const ROOT: LocalExpnId = LocalExpnId::from_u32(0); + pub const ROOT: LocalExpnId = LocalExpnId::ZERO; #[inline] fn from_raw(idx: ExpnIndex) -> LocalExpnId { @@ -242,7 +242,7 @@ impl ExpnId { /// The ID of the theoretical expansion that generates freshly parsed, unexpanded AST. /// Invariant: we do not create any ExpnId with local_id == 0 and krate != 0. pub const fn root() -> ExpnId { - ExpnId { krate: LOCAL_CRATE, local_id: ExpnIndex::from_u32(0) } + ExpnId { krate: LOCAL_CRATE, local_id: ExpnIndex::ZERO } } #[inline] diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 0c974ef4ca3eb..7ce879807cae1 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -271,6 +271,18 @@ impl RealFileName { } } + /// Return the path remmapped or not depending on the [`FileNameDisplayPreference`]. + /// + /// For the purpose of this function, local and short preference are equal. + pub fn to_path(&self, display_pref: FileNameDisplayPreference) -> &Path { + match display_pref { + FileNameDisplayPreference::Local | FileNameDisplayPreference::Short => { + self.local_path_if_available() + } + FileNameDisplayPreference::Remapped => self.remapped_path_if_available(), + } + } + pub fn to_string_lossy(&self, display_pref: FileNameDisplayPreference) -> Cow<'_, str> { match display_pref { FileNameDisplayPreference::Local => self.local_path_if_available().to_string_lossy(), diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index df7635e447da8..770624d331d1e 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -1129,6 +1129,21 @@ impl FilePathMapping { } } + /// Applies any path prefix substitution as defined by the mapping. + /// The return value is the local path with a "virtual path" representing the remapped + /// part if any remapping was performed. + pub fn to_real_filename<'a>(&self, local_path: impl Into>) -> RealFileName { + let local_path = local_path.into(); + if let (remapped_path, true) = self.map_prefix(&*local_path) { + RealFileName::Remapped { + virtual_name: remapped_path.into_owned(), + local_path: Some(local_path.into_owned()), + } + } else { + RealFileName::LocalPath(local_path.into_owned()) + } + } + /// Expand a relative path to an absolute path with remapping taken into account. /// Use this when absolute paths are required (e.g. debuginfo or crate metadata). /// diff --git a/compiler/rustc_span/src/source_map/tests.rs b/compiler/rustc_span/src/source_map/tests.rs index 81a9e4706883f..dcb02da371921 100644 --- a/compiler/rustc_span/src/source_map/tests.rs +++ b/compiler/rustc_span/src/source_map/tests.rs @@ -243,7 +243,7 @@ fn t10() { src_hash, stable_id, source_len.to_u32(), - CrateNum::new(0), + CrateNum::ZERO, FreezeLock::new(lines.read().clone()), multibyte_chars, non_narrow_chars, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 891ddb7af5b0b..ea0f7adf6f916 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -531,8 +531,15 @@ symbols! { cmp, cmp_max, cmp_min, + cmp_ord_max, + cmp_ord_min, cmp_partialeq_eq, cmp_partialeq_ne, + cmp_partialord_cmp, + cmp_partialord_ge, + cmp_partialord_gt, + cmp_partialord_le, + cmp_partialord_lt, cmpxchg16b_target_feature, cmse_nonsecure_entry, coerce_unsized, @@ -1185,6 +1192,7 @@ symbols! { multiple_supertrait_upcastable, must_not_suspend, must_use, + mut_ref, naked, naked_functions, name, @@ -1297,6 +1305,24 @@ symbols! { panic_abort, panic_bounds_check, panic_cannot_unwind, + panic_const_add_overflow, + panic_const_async_fn_resumed, + panic_const_async_fn_resumed_panic, + panic_const_async_gen_fn_resumed, + panic_const_async_gen_fn_resumed_panic, + panic_const_coroutine_resumed, + panic_const_coroutine_resumed_panic, + panic_const_div_by_zero, + panic_const_div_overflow, + panic_const_gen_fn_none, + panic_const_gen_fn_none_panic, + panic_const_mul_overflow, + panic_const_neg_overflow, + panic_const_rem_by_zero, + panic_const_rem_overflow, + panic_const_shl_overflow, + panic_const_shr_overflow, + panic_const_sub_overflow, panic_fmt, panic_handler, panic_impl, @@ -1430,6 +1456,7 @@ symbols! { receiver, recursion_limit, reexport_test_harness_main, + ref_pat_everywhere, ref_unwind_safe_trait, reference, reflect, @@ -1572,6 +1599,7 @@ symbols! { rustc_peek_maybe_init, rustc_peek_maybe_uninit, rustc_polymorphize_error, + rustc_preserve_ub_checks, rustc_private, rustc_proc_macro_decls, rustc_promotable, @@ -1632,7 +1660,7 @@ symbols! { simd_cttz, simd_div, simd_eq, - simd_expose_addr, + simd_expose_provenance, simd_extract, simd_fabs, simd_fcos, @@ -1648,7 +1676,6 @@ symbols! { simd_fmin, simd_fpow, simd_fpowi, - simd_from_exposed_addr, simd_fsin, simd_fsqrt, simd_gather, @@ -1687,6 +1714,7 @@ symbols! { simd_shuffle_generic, simd_sub, simd_trunc, + simd_with_exposed_provenance, simd_xor, since, sinf128, @@ -1786,6 +1814,7 @@ symbols! { thread, thread_local, thread_local_macro, + three_way_compare, thumb2, thumb_mode: "thumb-mode", tmm_reg, diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index 1c62ce2d214b7..f68668a91e688 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -2,7 +2,7 @@ use rustc_data_structures::stable_hasher::{Hash64, HashStable, StableHasher}; use rustc_hir::def_id::CrateNum; use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; use rustc_middle::ty::print::{PrettyPrinter, Print, PrintError, Printer}; -use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, Instance, ReifyReason, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::ty::{GenericArg, GenericArgKind}; use std::fmt::{self, Write}; @@ -71,8 +71,14 @@ pub(super) fn mangle<'tcx>( ty::InstanceDef::VTableShim(..) => { printer.write_str("{{vtable-shim}}").unwrap(); } - ty::InstanceDef::ReifyShim(..) => { - printer.write_str("{{reify-shim}}").unwrap(); + ty::InstanceDef::ReifyShim(_, reason) => { + printer.write_str("{{reify-shim").unwrap(); + match reason { + Some(ReifyReason::FnPtr) => printer.write_str("-fnptr").unwrap(), + Some(ReifyReason::Vtable) => printer.write_str("-vtable").unwrap(), + None => (), + } + printer.write_str("}}").unwrap(); } // FIXME(async_closures): This shouldn't be needed when we fix // `Instance::ty`/`Instance::def_id`. diff --git a/compiler/rustc_symbol_mangling/src/typeid.rs b/compiler/rustc_symbol_mangling/src/typeid.rs index 1d28d2b732fdf..862ba285db807 100644 --- a/compiler/rustc_symbol_mangling/src/typeid.rs +++ b/compiler/rustc_symbol_mangling/src/typeid.rs @@ -4,7 +4,7 @@ /// For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, /// see design document in the tracking issue #89653. use bitflags::bitflags; -use rustc_middle::ty::{Instance, Ty, TyCtxt}; +use rustc_middle::ty::{Instance, InstanceDef, ReifyReason, Ty, TyCtxt}; use rustc_target::abi::call::FnAbi; use std::hash::Hasher; use twox_hash::XxHash64; @@ -13,9 +13,20 @@ bitflags! { /// Options for typeid_for_fnabi. #[derive(Clone, Copy, Debug)] pub struct TypeIdOptions: u32 { + /// Generalizes pointers for compatibility with Clang + /// `-fsanitize-cfi-icall-generalize-pointers` option for cross-language LLVM CFI and KCFI + /// support. const GENERALIZE_POINTERS = 1; + /// Generalizes repr(C) user-defined type for extern function types with the "C" calling + /// convention (or extern types) for cross-language LLVM CFI and KCFI support. const GENERALIZE_REPR_C = 2; + /// Normalizes integers for compatibility with Clang + /// `-fsanitize-cfi-icall-experimental-normalize-integers` option for cross-language LLVM + /// CFI and KCFI support. const NORMALIZE_INTEGERS = 4; + /// Generalize the instance by erasing the concrete `Self` type where possible. + /// Only has an effect on `{kcfi_,}typeid_for_instance`. + const ERASE_SELF_TYPE = 8; } } @@ -56,8 +67,13 @@ pub fn kcfi_typeid_for_fnabi<'tcx>( pub fn kcfi_typeid_for_instance<'tcx>( tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, - options: TypeIdOptions, + mut options: TypeIdOptions, ) -> u32 { + // If we receive a `ReifyShim` intended to produce a function pointer, we need to remain + // concrete - abstraction is for vtables. + if matches!(instance.def, InstanceDef::ReifyShim(_, Some(ReifyReason::FnPtr))) { + options.remove(TypeIdOptions::ERASE_SELF_TYPE); + } // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.) let mut hash: XxHash64 = Default::default(); diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs index 04b92fbd33bf2..c632712f5a917 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs @@ -10,13 +10,15 @@ use rustc_data_structures::base_n; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; +use rustc_hir::lang_items::LangItem; +use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::layout::IntegerExt; -use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{ self, Const, ExistentialPredicate, FloatTy, FnSig, Instance, IntTy, List, Region, RegionKind, TermKind, Ty, TyCtxt, UintTy, }; use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgsRef}; +use rustc_middle::ty::{TypeFoldable, TypeVisitableExt}; use rustc_span::def_id::DefId; use rustc_span::sym; use rustc_target::abi::call::{Conv, FnAbi, PassMode}; @@ -181,14 +183,15 @@ fn encode_fnsig<'tcx>( // Encode the return type let transform_ty_options = TransformTyOptions::from_bits(options.bits()) .unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits())); - let ty = transform_ty(tcx, fn_sig.output(), &mut Vec::new(), transform_ty_options); + let mut type_folder = TransformTy::new(tcx, transform_ty_options); + let ty = fn_sig.output().fold_with(&mut type_folder); s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options)); // Encode the parameter types let tys = fn_sig.inputs(); if !tys.is_empty() { for ty in tys { - let ty = transform_ty(tcx, *ty, &mut Vec::new(), transform_ty_options); + let ty = ty.fold_with(&mut type_folder); s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options)); } @@ -522,15 +525,9 @@ fn encode_ty<'tcx>( ty::Array(ty0, len) => { // A + let len = len.eval_target_usize(tcx, ty::ParamEnv::reveal_all()); let mut s = String::from("A"); - let _ = write!( - s, - "{}", - &len.try_to_scalar() - .unwrap() - .to_target_usize(&tcx.data_layout) - .expect("Array lens are defined in usize") - ); + let _ = write!(s, "{}", &len); s.push_str(&encode_ty(tcx, *ty0, dict, options)); compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); typeid.push_str(&s); @@ -641,9 +638,7 @@ fn encode_ty<'tcx>( } // Function types - ty::FnDef(def_id, args) - | ty::Closure(def_id, args) - | ty::CoroutineClosure(def_id, args) => { + ty::FnDef(def_id, args) | ty::Closure(def_id, args) => { // u[IE], where is , // as vendor extended type. let mut s = String::new(); @@ -654,6 +649,18 @@ fn encode_ty<'tcx>( typeid.push_str(&s); } + ty::CoroutineClosure(def_id, args) => { + // u[IE], where is , + // as vendor extended type. + let mut s = String::new(); + let name = encode_ty_name(tcx, *def_id); + let _ = write!(s, "u{}{}", name.len(), &name); + let parent_args = tcx.mk_args(args.as_coroutine_closure().parent_args()); + s.push_str(&encode_args(tcx, parent_args, dict, options)); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + ty::Coroutine(def_id, args, ..) => { // u[IE], where is , // as vendor extended type. @@ -745,278 +752,208 @@ fn encode_ty<'tcx>( typeid } -/// Transforms predicates for being encoded and used in the substitution dictionary. -fn transform_predicates<'tcx>( +struct TransformTy<'tcx> { tcx: TyCtxt<'tcx>, - predicates: &List>, -) -> &'tcx List> { - tcx.mk_poly_existential_predicates_from_iter(predicates.iter().filter_map(|predicate| { - match predicate.skip_binder() { - ty::ExistentialPredicate::Trait(trait_ref) => { - let trait_ref = ty::TraitRef::identity(tcx, trait_ref.def_id); - Some(ty::Binder::dummy(ty::ExistentialPredicate::Trait( - ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref), - ))) - } - ty::ExistentialPredicate::Projection(..) => None, - ty::ExistentialPredicate::AutoTrait(..) => Some(predicate), - } - })) -} - -/// Transforms args for being encoded and used in the substitution dictionary. -fn transform_args<'tcx>( - tcx: TyCtxt<'tcx>, - args: GenericArgsRef<'tcx>, - parents: &mut Vec>, options: TransformTyOptions, -) -> GenericArgsRef<'tcx> { - let args = args.iter().map(|arg| match arg.unpack() { - GenericArgKind::Type(ty) if ty.is_c_void(tcx) => Ty::new_unit(tcx).into(), - GenericArgKind::Type(ty) => transform_ty(tcx, ty, parents, options).into(), - _ => arg, - }); - tcx.mk_args_from_iter(args) + parents: Vec>, } -// Transforms a ty:Ty for being encoded and used in the substitution dictionary. It transforms all -// c_void types into unit types unconditionally, generalizes pointers if -// TransformTyOptions::GENERALIZE_POINTERS option is set, and normalizes integers if -// TransformTyOptions::NORMALIZE_INTEGERS option is set. -fn transform_ty<'tcx>( - tcx: TyCtxt<'tcx>, - mut ty: Ty<'tcx>, - parents: &mut Vec>, - options: TransformTyOptions, -) -> Ty<'tcx> { - match ty.kind() { - ty::Float(..) | ty::Str | ty::Never | ty::Foreign(..) | ty::CoroutineWitness(..) => {} +impl<'tcx> TransformTy<'tcx> { + fn new(tcx: TyCtxt<'tcx>, options: TransformTyOptions) -> Self { + TransformTy { tcx, options, parents: Vec::new() } + } +} - ty::Bool => { - if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { - // Note: on all platforms that Rust's currently supports, its size and alignment are - // 1, and its ABI class is INTEGER - see Rust Layout and ABIs. - // - // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#bool.) - // - // Clang represents bool as an 8-bit unsigned integer. - ty = tcx.types.u8; +impl<'tcx> TypeFolder> for TransformTy<'tcx> { + // Transforms a ty:Ty for being encoded and used in the substitution dictionary. It transforms + // all c_void types into unit types unconditionally, generalizes pointers if + // TransformTyOptions::GENERALIZE_POINTERS option is set, and normalizes integers if + // TransformTyOptions::NORMALIZE_INTEGERS option is set. + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + match t.kind() { + ty::Array(..) + | ty::Closure(..) + | ty::Coroutine(..) + | ty::CoroutineClosure(..) + | ty::CoroutineWitness(..) + | ty::Float(..) + | ty::FnDef(..) + | ty::Foreign(..) + | ty::Never + | ty::Slice(..) + | ty::Str + | ty::Tuple(..) => t.super_fold_with(self), + + ty::Bool => { + if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { + // Note: on all platforms that Rust's currently supports, its size and alignment + // are 1, and its ABI class is INTEGER - see Rust Layout and ABIs. + // + // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#bool.) + // + // Clang represents bool as an 8-bit unsigned integer. + self.tcx.types.u8 + } else { + t + } } - } - ty::Char => { - if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { - // Since #118032, char is guaranteed to have the same size, alignment, and function - // call ABI as u32 on all platforms. - ty = tcx.types.u32; + ty::Char => { + if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { + // Since #118032, char is guaranteed to have the same size, alignment, and + // function call ABI as u32 on all platforms. + self.tcx.types.u32 + } else { + t + } } - } - ty::Int(..) | ty::Uint(..) => { - if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { - // Note: C99 7.18.2.4 requires uintptr_t and intptr_t to be at least 16-bit wide. - // All platforms we currently support have a C platform, and as a consequence, - // isize/usize are at least 16-bit wide for all of them. - // - // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#isize-and-usize.) - match ty.kind() { - ty::Int(IntTy::Isize) => match tcx.sess.target.pointer_width { - 16 => ty = tcx.types.i16, - 32 => ty = tcx.types.i32, - 64 => ty = tcx.types.i64, - 128 => ty = tcx.types.i128, - _ => bug!( - "transform_ty: unexpected pointer width `{}`", - tcx.sess.target.pointer_width - ), - }, - ty::Uint(UintTy::Usize) => match tcx.sess.target.pointer_width { - 16 => ty = tcx.types.u16, - 32 => ty = tcx.types.u32, - 64 => ty = tcx.types.u64, - 128 => ty = tcx.types.u128, - _ => bug!( - "transform_ty: unexpected pointer width `{}`", - tcx.sess.target.pointer_width - ), - }, - _ => (), + ty::Int(..) | ty::Uint(..) => { + if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { + // Note: C99 7.18.2.4 requires uintptr_t and intptr_t to be at least 16-bit + // wide. All platforms we currently support have a C platform, and as a + // consequence, isize/usize are at least 16-bit wide for all of them. + // + // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#isize-and-usize.) + match t.kind() { + ty::Int(IntTy::Isize) => match self.tcx.sess.target.pointer_width { + 16 => self.tcx.types.i16, + 32 => self.tcx.types.i32, + 64 => self.tcx.types.i64, + 128 => self.tcx.types.i128, + _ => bug!( + "fold_ty: unexpected pointer width `{}`", + self.tcx.sess.target.pointer_width + ), + }, + ty::Uint(UintTy::Usize) => match self.tcx.sess.target.pointer_width { + 16 => self.tcx.types.u16, + 32 => self.tcx.types.u32, + 64 => self.tcx.types.u64, + 128 => self.tcx.types.u128, + _ => bug!( + "fold_ty: unexpected pointer width `{}`", + self.tcx.sess.target.pointer_width + ), + }, + _ => t, + } + } else { + t } } - } - - _ if ty.is_unit() => {} - - ty::Tuple(tys) => { - ty = Ty::new_tup_from_iter( - tcx, - tys.iter().map(|ty| transform_ty(tcx, ty, parents, options)), - ); - } - - ty::Array(ty0, len) => { - let len = len.eval_target_usize(tcx, ty::ParamEnv::reveal_all()); - ty = Ty::new_array(tcx, transform_ty(tcx, *ty0, parents, options), len); - } - - ty::Slice(ty0) => { - ty = Ty::new_slice(tcx, transform_ty(tcx, *ty0, parents, options)); - } + ty::Adt(..) if t.is_c_void(self.tcx) => self.tcx.types.unit, - ty::Adt(adt_def, args) => { - if ty.is_c_void(tcx) { - ty = Ty::new_unit(tcx); - } else if options.contains(TransformTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c() - { - ty = Ty::new_adt(tcx, *adt_def, ty::List::empty()); - } else if adt_def.repr().transparent() && adt_def.is_struct() && !parents.contains(&ty) - { - // Don't transform repr(transparent) types with an user-defined CFI encoding to - // preserve the user-defined CFI encoding. - if let Some(_) = tcx.get_attr(adt_def.did(), sym::cfi_encoding) { - return ty; - } - let variant = adt_def.non_enum_variant(); - let param_env = tcx.param_env(variant.def_id); - let field = variant.fields.iter().find(|field| { - let ty = tcx.type_of(field.did).instantiate_identity(); - let is_zst = - tcx.layout_of(param_env.and(ty)).is_ok_and(|layout| layout.is_zst()); - !is_zst - }); - if let Some(field) = field { - let ty0 = tcx.type_of(field.did).instantiate(tcx, args); - // Generalize any repr(transparent) user-defined type that is either a pointer - // or reference, and either references itself or any other type that contains or - // references itself, to avoid a reference cycle. - - // If the self reference is not through a pointer, for example, due - // to using `PhantomData`, need to skip normalizing it if we hit it again. - parents.push(ty); - if ty0.is_any_ptr() && ty0.contains(ty) { - ty = transform_ty( - tcx, - ty0, - parents, - options | TransformTyOptions::GENERALIZE_POINTERS, - ); + ty::Adt(adt_def, args) => { + if adt_def.repr().transparent() && adt_def.is_struct() && !self.parents.contains(&t) + { + // Don't transform repr(transparent) types with an user-defined CFI encoding to + // preserve the user-defined CFI encoding. + if let Some(_) = self.tcx.get_attr(adt_def.did(), sym::cfi_encoding) { + return t; + } + let variant = adt_def.non_enum_variant(); + let param_env = self.tcx.param_env(variant.def_id); + let field = variant.fields.iter().find(|field| { + let ty = self.tcx.type_of(field.did).instantiate_identity(); + let is_zst = self + .tcx + .layout_of(param_env.and(ty)) + .is_ok_and(|layout| layout.is_zst()); + !is_zst + }); + if let Some(field) = field { + let ty0 = self.tcx.type_of(field.did).instantiate(self.tcx, args); + // Generalize any repr(transparent) user-defined type that is either a + // pointer or reference, and either references itself or any other type that + // contains or references itself, to avoid a reference cycle. + + // If the self reference is not through a pointer, for example, due + // to using `PhantomData`, need to skip normalizing it if we hit it again. + self.parents.push(t); + let ty = if ty0.is_any_ptr() && ty0.contains(t) { + let options = self.options; + self.options |= TransformTyOptions::GENERALIZE_POINTERS; + let ty = ty0.fold_with(self); + self.options = options; + ty + } else { + ty0.fold_with(self) + }; + self.parents.pop(); + ty } else { - ty = transform_ty(tcx, ty0, parents, options); + // Transform repr(transparent) types without non-ZST field into () + self.tcx.types.unit } - parents.pop(); } else { - // Transform repr(transparent) types without non-ZST field into () - ty = Ty::new_unit(tcx); + t.super_fold_with(self) } - } else { - ty = Ty::new_adt(tcx, *adt_def, transform_args(tcx, args, parents, options)); } - } - - ty::FnDef(def_id, args) => { - ty = Ty::new_fn_def(tcx, *def_id, transform_args(tcx, args, parents, options)); - } - ty::Closure(def_id, args) => { - ty = Ty::new_closure(tcx, *def_id, transform_args(tcx, args, parents, options)); - } - - ty::CoroutineClosure(def_id, args) => { - ty = Ty::new_coroutine_closure( - tcx, - *def_id, - transform_args(tcx, args, parents, options), - ); - } - - ty::Coroutine(def_id, args) => { - ty = Ty::new_coroutine(tcx, *def_id, transform_args(tcx, args, parents, options)); - } - - ty::Ref(region, ty0, ..) => { - if options.contains(TransformTyOptions::GENERALIZE_POINTERS) { - if ty.is_mutable_ptr() { - ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_static, Ty::new_unit(tcx)); - } else { - ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, Ty::new_unit(tcx)); - } - } else { - if ty.is_mutable_ptr() { - ty = Ty::new_mut_ref(tcx, *region, transform_ty(tcx, *ty0, parents, options)); + ty::Ref(..) => { + if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) { + if t.is_mutable_ptr() { + Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit) + } else { + Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit) + } } else { - ty = Ty::new_imm_ref(tcx, *region, transform_ty(tcx, *ty0, parents, options)); + t.super_fold_with(self) } } - } - ty::RawPtr(ptr_ty, _) => { - if options.contains(TransformTyOptions::GENERALIZE_POINTERS) { - if ty.is_mutable_ptr() { - ty = Ty::new_mut_ptr(tcx, Ty::new_unit(tcx)); + ty::RawPtr(..) => { + if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) { + if t.is_mutable_ptr() { + Ty::new_mut_ptr(self.tcx, self.tcx.types.unit) + } else { + Ty::new_imm_ptr(self.tcx, self.tcx.types.unit) + } } else { - ty = Ty::new_imm_ptr(tcx, Ty::new_unit(tcx)); + t.super_fold_with(self) } - } else { - if ty.is_mutable_ptr() { - ty = Ty::new_mut_ptr(tcx, transform_ty(tcx, *ptr_ty, parents, options)); + } + + ty::FnPtr(..) => { + if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) { + Ty::new_imm_ptr(self.tcx, self.tcx.types.unit) } else { - ty = Ty::new_imm_ptr(tcx, transform_ty(tcx, *ptr_ty, parents, options)); + t.super_fold_with(self) } } - } - ty::FnPtr(fn_sig) => { - if options.contains(TransformTyOptions::GENERALIZE_POINTERS) { - ty = Ty::new_imm_ptr(tcx, Ty::new_unit(tcx)); - } else { - let parameters: Vec> = fn_sig - .skip_binder() - .inputs() - .iter() - .map(|ty| transform_ty(tcx, *ty, parents, options)) - .collect(); - let output = transform_ty(tcx, fn_sig.skip_binder().output(), parents, options); - ty = Ty::new_fn_ptr( - tcx, - ty::Binder::bind_with_vars( - tcx.mk_fn_sig( - parameters, - output, - fn_sig.c_variadic(), - fn_sig.unsafety(), - fn_sig.abi(), - ), - fn_sig.bound_vars(), - ), + ty::Dynamic(predicates, _region, kind) => { + let predicates = self.tcx.mk_poly_existential_predicates_from_iter( + predicates.iter().filter_map(|predicate| match predicate.skip_binder() { + ty::ExistentialPredicate::Trait(trait_ref) => { + let trait_ref = ty::TraitRef::identity(self.tcx, trait_ref.def_id); + Some(ty::Binder::dummy(ty::ExistentialPredicate::Trait( + ty::ExistentialTraitRef::erase_self_ty(self.tcx, trait_ref), + ))) + } + ty::ExistentialPredicate::Projection(..) => None, + ty::ExistentialPredicate::AutoTrait(..) => Some(predicate), + }), ); - } - } - ty::Dynamic(predicates, _region, kind) => { - ty = Ty::new_dynamic( - tcx, - transform_predicates(tcx, predicates), - tcx.lifetimes.re_erased, - *kind, - ); - } + Ty::new_dynamic(self.tcx, predicates, self.tcx.lifetimes.re_erased, *kind) + } - ty::Alias(..) => { - ty = transform_ty( - tcx, - tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty), - parents, - options, - ); - } + ty::Alias(..) => { + self.fold_ty(self.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), t)) + } - ty::Bound(..) | ty::Error(..) | ty::Infer(..) | ty::Param(..) | ty::Placeholder(..) => { - bug!("transform_ty: unexpected `{:?}`", ty.kind()); + ty::Bound(..) | ty::Error(..) | ty::Infer(..) | ty::Param(..) | ty::Placeholder(..) => { + bug!("fold_ty: unexpected `{:?}`", t.kind()); + } } } - ty + fn interner(&self) -> TyCtxt<'tcx> { + self.tcx + } } /// Returns a type metadata identifier for the specified FnAbi using the Itanium C++ ABI with vendor @@ -1057,7 +994,8 @@ pub fn typeid_for_fnabi<'tcx>( // Encode the return type let transform_ty_options = TransformTyOptions::from_bits(options.bits()) .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits())); - let ty = transform_ty(tcx, fn_abi.ret.layout.ty, &mut Vec::new(), transform_ty_options); + let mut type_folder = TransformTy::new(tcx, transform_ty_options); + let ty = fn_abi.ret.layout.ty.fold_with(&mut type_folder); typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); // Encode the parameter types @@ -1069,7 +1007,7 @@ pub fn typeid_for_fnabi<'tcx>( let mut pushed_arg = false; for arg in fn_abi.args.iter().filter(|arg| arg.mode != PassMode::Ignore) { pushed_arg = true; - let ty = transform_ty(tcx, arg.layout.ty, &mut Vec::new(), transform_ty_options); + let ty = arg.layout.ty.fold_with(&mut type_folder); typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); } if !pushed_arg { @@ -1082,8 +1020,7 @@ pub fn typeid_for_fnabi<'tcx>( if fn_abi.args[n].mode == PassMode::Ignore { continue; } - let ty = - transform_ty(tcx, fn_abi.args[n].layout.ty, &mut Vec::new(), transform_ty_options); + let ty = fn_abi.args[n].layout.ty.fold_with(&mut type_folder); typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); } @@ -1140,44 +1077,115 @@ pub fn typeid_for_instance<'tcx>( let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]); let self_ty = Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased, ty::Dyn); instance.args = tcx.mk_args_trait(self_ty, List::empty()); - } else if matches!(instance.def, ty::InstanceDef::Virtual(..)) { - instance.args = strip_receiver_auto(tcx, instance.args); + } else if let ty::InstanceDef::Virtual(def_id, _) = instance.def { + let upcast_ty = match tcx.trait_of_item(def_id) { + Some(trait_id) => trait_object_ty( + tcx, + ty::Binder::dummy(ty::TraitRef::from_method(tcx, trait_id, instance.args)), + ), + // drop_in_place won't have a defining trait, skip the upcast + None => instance.args.type_at(0), + }; + let stripped_ty = strip_receiver_auto(tcx, upcast_ty); + instance.args = tcx.mk_args_trait(stripped_ty, instance.args.into_iter().skip(1)); + } else if let ty::InstanceDef::VTableShim(def_id) = instance.def + && let Some(trait_id) = tcx.trait_of_item(def_id) + { + // VTableShims may have a trait method, but a concrete Self. This is not suitable for a vtable, + // as the caller will not know the concrete Self. + let trait_ref = ty::TraitRef::new(tcx, trait_id, instance.args); + let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref)); + instance.args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); } - if let Some(impl_id) = tcx.impl_of_method(instance.def_id()) - && let Some(trait_ref) = tcx.impl_trait_ref(impl_id) - { - let impl_method = tcx.associated_item(instance.def_id()); - let method_id = impl_method - .trait_item_def_id - .expect("Part of a trait implementation, but not linked to the def_id?"); - let trait_method = tcx.associated_item(method_id); - let trait_id = trait_ref.skip_binder().def_id; - if traits::is_vtable_safe_method(tcx, trait_id, trait_method) - && tcx.object_safety_violations(trait_id).is_empty() + if options.contains(EncodeTyOptions::ERASE_SELF_TYPE) { + if let Some(impl_id) = tcx.impl_of_method(instance.def_id()) + && let Some(trait_ref) = tcx.impl_trait_ref(impl_id) { - // Trait methods will have a Self polymorphic parameter, where the concreteized - // implementatation will not. We need to walk back to the more general trait method - let trait_ref = tcx.instantiate_and_normalize_erasing_regions( - instance.args, - ty::ParamEnv::reveal_all(), - trait_ref, - ); + let impl_method = tcx.associated_item(instance.def_id()); + let method_id = impl_method + .trait_item_def_id + .expect("Part of a trait implementation, but not linked to the def_id?"); + let trait_method = tcx.associated_item(method_id); + let trait_id = trait_ref.skip_binder().def_id; + if traits::is_vtable_safe_method(tcx, trait_id, trait_method) + && tcx.object_safety_violations(trait_id).is_empty() + { + // Trait methods will have a Self polymorphic parameter, where the concreteized + // implementatation will not. We need to walk back to the more general trait method + let trait_ref = tcx.instantiate_and_normalize_erasing_regions( + instance.args, + ty::ParamEnv::reveal_all(), + trait_ref, + ); + let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref)); + + // At the call site, any call to this concrete function through a vtable will be + // `Virtual(method_id, idx)` with appropriate arguments for the method. Since we have the + // original method id, and we've recovered the trait arguments, we can make the callee + // instance we're computing the alias set for match the caller instance. + // + // Right now, our code ignores the vtable index everywhere, so we use 0 as a placeholder. + // If we ever *do* start encoding the vtable index, we will need to generate an alias set + // based on which vtables we are putting this method into, as there will be more than one + // index value when supertraits are involved. + instance.def = ty::InstanceDef::Virtual(method_id, 0); + let abstract_trait_args = + tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); + instance.args = instance.args.rebase_onto(tcx, impl_id, abstract_trait_args); + } + } else if tcx.is_closure_like(instance.def_id()) { + // We're either a closure or a coroutine. Our goal is to find the trait we're defined on, + // instantiate it, and take the type of its only method as our own. + let closure_ty = instance.ty(tcx, ty::ParamEnv::reveal_all()); + let (trait_id, inputs) = match closure_ty.kind() { + ty::Closure(..) => { + let closure_args = instance.args.as_closure(); + let trait_id = tcx.fn_trait_kind_to_def_id(closure_args.kind()).unwrap(); + let tuple_args = + tcx.instantiate_bound_regions_with_erased(closure_args.sig()).inputs()[0]; + (trait_id, Some(tuple_args)) + } + ty::Coroutine(..) => match tcx.coroutine_kind(instance.def_id()).unwrap() { + hir::CoroutineKind::Coroutine(..) => ( + tcx.require_lang_item(LangItem::Coroutine, None), + Some(instance.args.as_coroutine().resume_ty()), + ), + hir::CoroutineKind::Desugared(desugaring, _) => { + let lang_item = match desugaring { + hir::CoroutineDesugaring::Async => LangItem::Future, + hir::CoroutineDesugaring::AsyncGen => LangItem::AsyncIterator, + hir::CoroutineDesugaring::Gen => LangItem::Iterator, + }; + (tcx.require_lang_item(lang_item, None), None) + } + }, + ty::CoroutineClosure(..) => ( + tcx.require_lang_item(LangItem::FnOnce, None), + Some( + tcx.instantiate_bound_regions_with_erased( + instance.args.as_coroutine_closure().coroutine_closure_sig(), + ) + .tupled_inputs_ty, + ), + ), + x => bug!("Unexpected type kind for closure-like: {x:?}"), + }; + let concrete_args = tcx.mk_args_trait(closure_ty, inputs.map(Into::into)); + let trait_ref = ty::TraitRef::new(tcx, trait_id, concrete_args); let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref)); + let abstract_args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); + // There should be exactly one method on this trait, and it should be the one we're + // defining. + let call = tcx + .associated_items(trait_id) + .in_definition_order() + .find(|it| it.kind == ty::AssocKind::Fn) + .expect("No call-family function on closure-like Fn trait?") + .def_id; - // At the call site, any call to this concrete function through a vtable will be - // `Virtual(method_id, idx)` with appropriate arguments for the method. Since we have the - // original method id, and we've recovered the trait arguments, we can make the callee - // instance we're computing the alias set for match the caller instance. - // - // Right now, our code ignores the vtable index everywhere, so we use 0 as a placeholder. - // If we ever *do* start encoding the vtable index, we will need to generate an alias set - // based on which vtables we are putting this method into, as there will be more than one - // index value when supertraits are involved. - instance.def = ty::InstanceDef::Virtual(method_id, 0); - let abstract_trait_args = - tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); - instance.args = instance.args.rebase_onto(tcx, impl_id, abstract_trait_args); + instance.def = ty::InstanceDef::Virtual(call, 0); + instance.args = abstract_args; } } @@ -1190,15 +1198,11 @@ pub fn typeid_for_instance<'tcx>( typeid_for_fnabi(tcx, fn_abi, options) } -fn strip_receiver_auto<'tcx>( - tcx: TyCtxt<'tcx>, - args: ty::GenericArgsRef<'tcx>, -) -> ty::GenericArgsRef<'tcx> { - let ty = args.type_at(0); +fn strip_receiver_auto<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { let ty::Dynamic(preds, lifetime, kind) = ty.kind() else { bug!("Tried to strip auto traits from non-dynamic type {ty}"); }; - let new_rcvr = if preds.principal().is_some() { + if preds.principal().is_some() { let filtered_preds = tcx.mk_poly_existential_predicates_from_iter(preds.into_iter().filter(|pred| { !matches!(pred.skip_binder(), ty::ExistentialPredicate::AutoTrait(..)) @@ -1209,8 +1213,7 @@ fn strip_receiver_auto<'tcx>( // about it. This technically discards the knowledge that it was a type that was made // into a trait object at some point, but that's not a lot. tcx.types.unit - }; - tcx.mk_args_trait(new_rcvr, args.into_iter().skip(1)) + } } #[instrument(skip(tcx), ret)] diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 4369f020d27a3..8cb5370bb4a87 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -8,8 +8,8 @@ use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::print::{Print, PrintError, Printer}; use rustc_middle::ty::{ - self, EarlyBinder, FloatTy, Instance, IntTy, Ty, TyCtxt, TypeVisitable, TypeVisitableExt, - UintTy, + self, EarlyBinder, FloatTy, Instance, IntTy, ReifyReason, Ty, TyCtxt, TypeVisitable, + TypeVisitableExt, UintTy, }; use rustc_middle::ty::{GenericArg, GenericArgKind}; use rustc_span::symbol::kw; @@ -44,7 +44,9 @@ pub(super) fn mangle<'tcx>( let shim_kind = match instance.def { ty::InstanceDef::ThreadLocalShim(_) => Some("tls"), ty::InstanceDef::VTableShim(_) => Some("vtable"), - ty::InstanceDef::ReifyShim(_) => Some("reify"), + ty::InstanceDef::ReifyShim(_, None) => Some("reify"), + ty::InstanceDef::ReifyShim(_, Some(ReifyReason::FnPtr)) => Some("reify-fnptr"), + ty::InstanceDef::ReifyShim(_, Some(ReifyReason::Vtable)) => Some("reify-vtable"), ty::InstanceDef::ConstructCoroutineInClosureShim { .. } | ty::InstanceDef::CoroutineKindShim { .. } => Some("fn_once"), diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index 486afc5f8f30a..cdd3f0afd79a6 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -251,9 +251,9 @@ pub struct Uniform { /// The total size of the argument, which can be: /// * equal to `unit.size` (one scalar/vector), /// * a multiple of `unit.size` (an array of scalar/vectors), - /// * if `unit.kind` is `Integer`, the last element - /// can be shorter, i.e., `{ i64, i64, i32 }` for - /// 64-bit integers with a total size of 20 bytes. + /// * if `unit.kind` is `Integer`, the last element can be shorter, i.e., `{ i64, i64, i32 }` + /// for 64-bit integers with a total size of 20 bytes. When the argument is actually passed, + /// this size will be rounded up to the nearest multiple of `unit.size`. pub total: Size, } @@ -319,14 +319,17 @@ impl CastTarget { } pub fn size(&self, _cx: &C) -> Size { - let mut size = self.rest.total; - for i in 0..self.prefix.iter().count() { - match self.prefix[i] { - Some(v) => size += v.size, - None => {} - } - } - return size; + // Prefix arguments are passed in specific designated registers + let prefix_size = self + .prefix + .iter() + .filter_map(|x| x.map(|reg| reg.size)) + .fold(Size::ZERO, |acc, size| acc + size); + // Remaining arguments are passed in chunks of the unit size + let rest_size = + self.rest.unit.size * self.rest.total.bytes().div_ceil(self.rest.unit.size.bytes()); + + prefix_size + rest_size } pub fn align(&self, cx: &C) -> Align { @@ -927,7 +930,7 @@ impl FromStr for Conv { } // Some types are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index f694dd0070363..7056288e758c8 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -49,8 +49,7 @@ impl<'tcx> InferCtxt<'tcx> { /// - the parameter environment /// /// Invokes `evaluate_obligation`, so in the event that evaluating - /// `Ty: Trait` causes overflow, EvaluatedToErrStackDependent - /// (or EvaluatedToAmbigStackDependent) will be returned. + /// `Ty: Trait` causes overflow, EvaluatedToAmbigStackDependent will be returned. #[instrument(level = "debug", skip(self, params), ret)] fn type_implements_trait( &self, diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index e14fc62cd6f06..b5fb710e4cc42 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -30,7 +30,7 @@ #[macro_use] extern crate rustc_macros; -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] #[macro_use] extern crate rustc_data_structures; #[macro_use] diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs index e081a9100e2fc..f2c441dcbeda6 100644 --- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs +++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs @@ -2,8 +2,8 @@ //! Doing this via a separate goal is called "deferred alias relation" and part //! of our more general approach to "lazy normalization". //! -//! This is done by first normalizing both sides of the goal, ending up in -//! either a concrete type, rigid alias, or an infer variable. +//! This is done by first structurally normalizing both sides of the goal, ending +//! up in either a concrete type, rigid alias, or an infer variable. //! These are related further according to the rules below: //! //! (1.) If we end up with two rigid aliases, then we relate them structurally. @@ -14,18 +14,10 @@ //! //! (3.) Otherwise, if we end with two rigid (non-projection) or infer types, //! relate them structurally. -//! -//! Subtle: when relating an opaque to another type, we emit a -//! `NormalizesTo(opaque, ?fresh_var)` goal when trying to normalize the opaque. -//! This nested goal starts out as ambiguous and does not actually define the opaque. -//! However, if `?fresh_var` ends up geteting equated to another type, we retry the -//! `NormalizesTo` goal, at which point the opaque is actually defined. use super::EvalCtxt; -use rustc_infer::traits::query::NoSolution; -use rustc_infer::traits::solve::GoalSource; use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty; impl<'tcx> EvalCtxt<'_, 'tcx> { #[instrument(level = "debug", skip(self), ret)] @@ -36,21 +28,34 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let tcx = self.tcx(); let Goal { param_env, predicate: (lhs, rhs, direction) } = goal; - let Some(lhs) = self.try_normalize_term(param_env, lhs)? else { - return self - .evaluate_added_goals_and_make_canonical_response(Certainty::overflow(true)); + // Structurally normalize the lhs. + let lhs = if let Some(alias) = lhs.to_alias_ty(self.tcx()) { + let term = self.next_term_infer_of_kind(lhs); + self.add_normalizes_to_goal(goal.with(tcx, ty::NormalizesTo { alias, term })); + term + } else { + lhs }; - let Some(rhs) = self.try_normalize_term(param_env, rhs)? else { - return self - .evaluate_added_goals_and_make_canonical_response(Certainty::overflow(true)); + // Structurally normalize the rhs. + let rhs = if let Some(alias) = rhs.to_alias_ty(self.tcx()) { + let term = self.next_term_infer_of_kind(rhs); + self.add_normalizes_to_goal(goal.with(tcx, ty::NormalizesTo { alias, term })); + term + } else { + rhs }; + // Apply the constraints. + self.try_evaluate_added_goals()?; + let lhs = self.resolve_vars_if_possible(lhs); + let rhs = self.resolve_vars_if_possible(rhs); + debug!(?lhs, ?rhs); + let variance = match direction { ty::AliasRelationDirection::Equate => ty::Variance::Invariant, ty::AliasRelationDirection::Subtype => ty::Variance::Covariant, }; - match (lhs.to_alias_ty(tcx), rhs.to_alias_ty(tcx)) { (None, None) => { self.relate(param_env, lhs, variance, rhs)?; @@ -58,14 +63,18 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } (Some(alias), None) => { - self.relate_rigid_alias_non_alias(param_env, alias, variance, rhs) + self.relate_rigid_alias_non_alias(param_env, alias, variance, rhs)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + (None, Some(alias)) => { + self.relate_rigid_alias_non_alias( + param_env, + alias, + variance.xform(ty::Variance::Contravariant), + lhs, + )?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } - (None, Some(alias)) => self.relate_rigid_alias_non_alias( - param_env, - alias, - variance.xform(ty::Variance::Contravariant), - lhs, - ), (Some(alias_lhs), Some(alias_rhs)) => { self.relate(param_env, alias_lhs, variance, alias_rhs)?; @@ -73,104 +82,4 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } } - - /// Relate a rigid alias with another type. This is the same as - /// an ordinary relate except that we treat the outer most alias - /// constructor as rigid. - #[instrument(level = "debug", skip(self, param_env), ret)] - fn relate_rigid_alias_non_alias( - &mut self, - param_env: ty::ParamEnv<'tcx>, - alias: ty::AliasTy<'tcx>, - variance: ty::Variance, - term: ty::Term<'tcx>, - ) -> QueryResult<'tcx> { - // NOTE: this check is purely an optimization, the structural eq would - // always fail if the term is not an inference variable. - if term.is_infer() { - let tcx = self.tcx(); - // We need to relate `alias` to `term` treating only the outermost - // constructor as rigid, relating any contained generic arguments as - // normal. We do this by first structurally equating the `term` - // with the alias constructor instantiated with unconstrained infer vars, - // and then relate this with the whole `alias`. - // - // Alternatively we could modify `Equate` for this case by adding another - // variant to `StructurallyRelateAliases`. - let identity_args = self.fresh_args_for_item(alias.def_id); - let rigid_ctor = ty::AliasTy::new(tcx, alias.def_id, identity_args); - self.eq_structurally_relating_aliases(param_env, term, rigid_ctor.to_ty(tcx).into())?; - self.eq(param_env, alias, rigid_ctor)?; - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } else { - Err(NoSolution) - } - } - - // FIXME: This needs a name that reflects that it's okay to bottom-out with an inference var. - /// Normalize the `term` to equate it later. - #[instrument(level = "debug", skip(self, param_env), ret)] - fn try_normalize_term( - &mut self, - param_env: ty::ParamEnv<'tcx>, - term: ty::Term<'tcx>, - ) -> Result>, NoSolution> { - match term.unpack() { - ty::TermKind::Ty(ty) => { - Ok(self.try_normalize_ty_recur(param_env, 0, ty).map(Into::into)) - } - ty::TermKind::Const(_) => { - if let Some(alias) = term.to_alias_ty(self.tcx()) { - let term = self.next_term_infer_of_kind(term); - self.add_normalizes_to_goal(Goal::new( - self.tcx(), - param_env, - ty::NormalizesTo { alias, term }, - )); - self.try_evaluate_added_goals()?; - Ok(Some(self.resolve_vars_if_possible(term))) - } else { - Ok(Some(term)) - } - } - } - } - - #[instrument(level = "debug", skip(self, param_env), ret)] - fn try_normalize_ty_recur( - &mut self, - param_env: ty::ParamEnv<'tcx>, - depth: usize, - ty: Ty<'tcx>, - ) -> Option> { - if !self.tcx().recursion_limit().value_within_limit(depth) { - return None; - } - - let ty::Alias(kind, alias) = *ty.kind() else { - return Some(ty); - }; - - match self.commit_if_ok(|this| { - let tcx = this.tcx(); - let normalized_ty = this.next_ty_infer(); - let normalizes_to = ty::NormalizesTo { alias, term: normalized_ty.into() }; - match kind { - ty::AliasKind::Opaque => { - // HACK: Unlike for associated types, `normalizes-to` for opaques - // is currently not treated as a function. We do not erase the - // expected term. - this.add_goal(GoalSource::Misc, Goal::new(tcx, param_env, normalizes_to)); - } - ty::AliasKind::Projection | ty::AliasKind::Inherent | ty::AliasKind::Weak => { - this.add_normalizes_to_goal(Goal::new(tcx, param_env, normalizes_to)) - } - } - this.try_evaluate_added_goals()?; - Ok(this.resolve_vars_if_possible(normalized_ty)) - }) { - Ok(ty) => self.try_normalize_ty_recur(param_env, depth + 1, ty), - Err(NoSolution) => Some(ty), - } - } } diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 5e580df01cbe4..35f7d1d715102 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -312,11 +312,18 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { fn forced_ambiguity(&mut self, cause: MaybeCause) -> Vec> { let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc); let certainty = Certainty::Maybe(cause); - let result = self.evaluate_added_goals_and_make_canonical_response(certainty).unwrap(); + // This may fail if `try_evaluate_added_goals` overflows because it + // fails to reach a fixpoint but ends up getting an error after + // running for some additional step. + // + // FIXME: Add a test for this. It seems to be necessary for typenum but + // is incredibly hard to minimize as it may rely on being inside of a + // trait solver cycle. + let result = self.evaluate_added_goals_and_make_canonical_response(certainty); let mut dummy_probe = self.inspect.new_probe(); - dummy_probe.probe_kind(ProbeKind::TraitCandidate { source, result: Ok(result) }); + dummy_probe.probe_kind(ProbeKind::TraitCandidate { source, result }); self.inspect.finish_probe(dummy_probe); - vec![Candidate { source, result }] + if let Ok(result) = result { vec![Candidate { source, result }] } else { vec![] } } #[instrument(level = "debug", skip_all)] diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs index 619435d2e8d50..4a4efb6884fc5 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -332,7 +332,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// whether an alias is rigid by using the trait solver. When instantiating a response /// from the solver we assume that the solver correctly handled aliases and therefore /// always relate them structurally here. - #[instrument(level = "debug", skip(infcx), ret)] + #[instrument(level = "debug", skip(infcx))] fn unify_query_var_values( infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs deleted file mode 100644 index c8f9a461adf52..0000000000000 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs +++ /dev/null @@ -1,47 +0,0 @@ -use super::{EvalCtxt, NestedGoals}; -use crate::solve::inspect; -use rustc_middle::traits::query::NoSolution; - -impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { - pub(in crate::solve) fn commit_if_ok( - &mut self, - f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> Result, - ) -> Result { - let mut nested_ecx = EvalCtxt { - infcx: self.infcx, - variables: self.variables, - var_values: self.var_values, - is_normalizes_to_goal: self.is_normalizes_to_goal, - predefined_opaques_in_body: self.predefined_opaques_in_body, - max_input_universe: self.max_input_universe, - search_graph: self.search_graph, - nested_goals: NestedGoals::new(), - tainted: self.tainted, - inspect: self.inspect.new_probe(), - }; - - let result = nested_ecx.infcx.commit_if_ok(|_| f(&mut nested_ecx)); - if result.is_ok() { - let EvalCtxt { - infcx: _, - variables: _, - var_values: _, - is_normalizes_to_goal: _, - predefined_opaques_in_body: _, - max_input_universe: _, - search_graph: _, - nested_goals, - tainted, - inspect, - } = nested_ecx; - self.nested_goals.extend(nested_goals); - self.tainted = tainted; - self.inspect.integrate_snapshot(inspect); - } else { - nested_ecx.inspect.probe_kind(inspect::ProbeKind::CommitIfOk); - self.inspect.finish_probe(nested_ecx.inspect); - } - - result - } -} diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs index a0fe6eca0fc8d..1739bd70e7b5a 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs @@ -24,7 +24,6 @@ use rustc_middle::ty::{ use rustc_session::config::DumpSolverProofTree; use rustc_span::DUMMY_SP; use std::io::Write; -use std::iter; use std::ops::ControlFlow; use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment}; @@ -36,7 +35,6 @@ use super::{GoalSource, SolverMode}; pub use select::InferCtxtSelectExt; mod canonical; -mod commit_if_ok; mod probe; mod select; @@ -124,11 +122,6 @@ impl<'tcx> NestedGoals<'tcx> { pub(super) fn is_empty(&self) -> bool { self.normalizes_to_goals.is_empty() && self.goals.is_empty() } - - pub(super) fn extend(&mut self, other: NestedGoals<'tcx>) { - self.normalizes_to_goals.extend(other.normalizes_to_goals); - self.goals.extend(other.goals) - } } #[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)] @@ -511,12 +504,6 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { self.inspect.evaluate_added_goals_loop_start(); - fn with_misc_source<'tcx>( - it: impl IntoIterator>>, - ) -> impl Iterator>)> { - iter::zip(iter::repeat(GoalSource::Misc), it) - } - // If this loop did not result in any progress, what's our final certainty. let mut unchanged_certainty = Some(Certainty::Yes); for goal in goals.normalizes_to_goals { @@ -534,16 +521,28 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { unconstrained_goal, )?; // Add the nested goals from normalization to our own nested goals. + debug!(?nested_goals); goals.goals.extend(nested_goals); // Finally, equate the goal's RHS with the unconstrained var. - // We put the nested goals from this into goals instead of - // next_goals to avoid needing to process the loop one extra - // time if this goal returns something -- I don't think this - // matters in practice, though. - let eq_goals = - self.eq_and_get_goals(goal.param_env, goal.predicate.term, unconstrained_rhs)?; - goals.goals.extend(with_misc_source(eq_goals)); + // + // SUBTLE: + // We structurally relate aliases here. This is necessary + // as we otherwise emit a nested `AliasRelate` goal in case the + // returned term is a rigid alias, resulting in overflow. + // + // It is correct as both `goal.predicate.term` and `unconstrained_rhs` + // start out as an unconstrained inference variable so any aliases get + // fully normalized when instantiating it. + // + // FIXME: Strictly speaking this may be incomplete if the normalized-to + // type contains an ambiguous alias referencing bound regions. We should + // consider changing this to only use "shallow structural equality". + self.eq_structurally_relating_aliases( + goal.param_env, + goal.predicate.term, + unconstrained_rhs, + )?; // We only look at the `projection_ty` part here rather than // looking at the "has changed" return from evaluate_goal, @@ -720,7 +719,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> Result<(), NoSolution> { self.infcx .at(&ObligationCause::dummy(), param_env) - .eq(DefineOpaqueTypes::No, lhs, rhs) + // New solver ignores DefineOpaqueTypes, so choose Yes for consistency + .eq(DefineOpaqueTypes::Yes, lhs, rhs) .map(|InferOk { value: (), obligations }| { self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into())); }) @@ -730,6 +730,46 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { }) } + /// This should be used when relating a rigid alias with another type. + /// + /// Normally we emit a nested `AliasRelate` when equating an inference + /// variable and an alias. This causes us to instead constrain the inference + /// variable to the alias without emitting a nested alias relate goals. + #[instrument(level = "debug", skip(self, param_env), ret)] + pub(super) fn relate_rigid_alias_non_alias( + &mut self, + param_env: ty::ParamEnv<'tcx>, + alias: ty::AliasTy<'tcx>, + variance: ty::Variance, + term: ty::Term<'tcx>, + ) -> Result<(), NoSolution> { + // NOTE: this check is purely an optimization, the structural eq would + // always fail if the term is not an inference variable. + if term.is_infer() { + let tcx = self.tcx(); + // We need to relate `alias` to `term` treating only the outermost + // constructor as rigid, relating any contained generic arguments as + // normal. We do this by first structurally equating the `term` + // with the alias constructor instantiated with unconstrained infer vars, + // and then relate this with the whole `alias`. + // + // Alternatively we could modify `Equate` for this case by adding another + // variant to `StructurallyRelateAliases`. + let identity_args = self.fresh_args_for_item(alias.def_id); + let rigid_ctor = ty::AliasTy::new(tcx, alias.def_id, identity_args); + let ctor_ty = rigid_ctor.to_ty(tcx); + let InferOk { value: (), obligations } = self + .infcx + .at(&ObligationCause::dummy(), param_env) + .trace(term, ctor_ty.into()) + .eq_structurally_relating_aliases(term, ctor_ty.into())?; + debug_assert!(obligations.is_empty()); + self.relate(param_env, alias, variance, rigid_ctor) + } else { + Err(NoSolution) + } + } + /// This sohuld only be used when we're either instantiating a previously /// unconstrained "return value" or when we're sure that all aliases in /// the types are rigid. @@ -759,7 +799,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> Result<(), NoSolution> { self.infcx .at(&ObligationCause::dummy(), param_env) - .sub(DefineOpaqueTypes::No, sub, sup) + // New solver ignores DefineOpaqueTypes, so choose Yes for consistency + .sub(DefineOpaqueTypes::Yes, sub, sup) .map(|InferOk { value: (), obligations }| { self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into())); }) @@ -779,7 +820,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> Result<(), NoSolution> { self.infcx .at(&ObligationCause::dummy(), param_env) - .relate(DefineOpaqueTypes::No, lhs, variance, rhs) + // New solver ignores DefineOpaqueTypes, so choose Yes for consistency + .relate(DefineOpaqueTypes::Yes, lhs, variance, rhs) .map(|InferOk { value: (), obligations }| { self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into())); }) @@ -803,7 +845,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> Result>>, NoSolution> { self.infcx .at(&ObligationCause::dummy(), param_env) - .eq(DefineOpaqueTypes::No, lhs, rhs) + // New solver ignores DefineOpaqueTypes, so choose Yes for consistency + .eq(DefineOpaqueTypes::Yes, lhs, rhs) .map(|InferOk { value: (), obligations }| { obligations.into_iter().map(|o| o.into()).collect() }) diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs index e0c7804b6db5f..6644d3c77afd9 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs @@ -182,7 +182,8 @@ fn rematch_impl<'tcx>( let mut nested = infcx .at(&ObligationCause::dummy(), goal.param_env) - .eq(DefineOpaqueTypes::No, goal.predicate.trait_ref, impl_trait_ref) + // New solver ignores DefineOpaqueTypes, so choose Yes for consistency + .eq(DefineOpaqueTypes::Yes, goal.predicate.trait_ref, impl_trait_ref) .map_err(|_| SelectionError::Unimplemented)? .into_obligations(); @@ -257,7 +258,8 @@ fn rematch_unsize<'tcx>( nested.extend( infcx .at(&ObligationCause::dummy(), goal.param_env) - .eq(DefineOpaqueTypes::No, a_elem_ty, b_elem_ty) + // New solver ignores DefineOpaqueTypes, so choose Yes for consistency + .eq(DefineOpaqueTypes::Yes, a_elem_ty, b_elem_ty) .expect("expected rematch to succeed") .into_obligations(), ); @@ -300,7 +302,8 @@ fn rematch_unsize<'tcx>( nested.extend( infcx .at(&ObligationCause::dummy(), goal.param_env) - .eq(DefineOpaqueTypes::No, unsized_a_ty, b_ty) + // New solver ignores DefineOpaqueTypes, so choose Yes for consistency + .eq(DefineOpaqueTypes::Yes, unsized_a_ty, b_ty) .expect("expected rematch to succeed") .into_obligations(), ); @@ -329,7 +332,8 @@ fn rematch_unsize<'tcx>( nested.extend( infcx .at(&ObligationCause::dummy(), goal.param_env) - .eq(DefineOpaqueTypes::No, unsized_a_ty, b_ty) + // New solver ignores DefineOpaqueTypes, so choose Yes for consistency + .eq(DefineOpaqueTypes::Yes, unsized_a_ty, b_ty) .expect("expected rematch to succeed") .into_obligations(), ); diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index cfec2e9bbf353..56c32d3d53916 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -130,17 +130,14 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { self.candidates_recur(candidates, nested_goals, probe); nested_goals.truncate(num_goals); } - inspect::ProbeStep::EvaluateGoals(_) - | inspect::ProbeStep::CommitIfOkStart - | inspect::ProbeStep::CommitIfOkSuccess => (), + inspect::ProbeStep::EvaluateGoals(_) => (), } } match probe.kind { inspect::ProbeKind::NormalizedSelfTyAssembly | inspect::ProbeKind::UnsizeAssembly - | inspect::ProbeKind::UpcastProjectionCompatibility - | inspect::ProbeKind::CommitIfOk => (), + | inspect::ProbeKind::UpcastProjectionCompatibility => (), // We add a candidate for the root evaluation if there // is only one way to prove a given goal, e.g. for `WellFormed`. // @@ -157,7 +154,8 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { }); } } - inspect::ProbeKind::MiscCandidate { name: _, result } + inspect::ProbeKind::TryNormalizeNonRigid { result } + | inspect::ProbeKind::MiscCandidate { name: _, result } | inspect::ProbeKind::TraitCandidate { source: _, result } => { candidates.push(InspectCandidate { goal: self, diff --git a/compiler/rustc_trait_selection/src/solve/inspect/build.rs b/compiler/rustc_trait_selection/src/solve/inspect/build.rs index 4da999f2406b9..43c76cc5f4a00 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/build.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/build.rs @@ -220,8 +220,6 @@ enum WipProbeStep<'tcx> { AddGoal(GoalSource, inspect::CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>), EvaluateGoals(WipAddedGoalsEvaluation<'tcx>), NestedProbe(WipProbe<'tcx>), - CommitIfOkStart, - CommitIfOkSuccess, } impl<'tcx> WipProbeStep<'tcx> { @@ -230,8 +228,6 @@ impl<'tcx> WipProbeStep<'tcx> { WipProbeStep::AddGoal(source, goal) => inspect::ProbeStep::AddGoal(source, goal), WipProbeStep::EvaluateGoals(eval) => inspect::ProbeStep::EvaluateGoals(eval.finalize()), WipProbeStep::NestedProbe(probe) => inspect::ProbeStep::NestedProbe(probe.finalize()), - WipProbeStep::CommitIfOkStart => inspect::ProbeStep::CommitIfOkStart, - WipProbeStep::CommitIfOkSuccess => inspect::ProbeStep::CommitIfOkSuccess, } } } @@ -467,29 +463,6 @@ impl<'tcx> ProofTreeBuilder<'tcx> { } } - /// Used by `EvalCtxt::commit_if_ok` to flatten the work done inside - /// of the probe into the parent. - pub fn integrate_snapshot(&mut self, probe: ProofTreeBuilder<'tcx>) { - if let Some(this) = self.as_mut() { - match (this, *probe.state.unwrap()) { - ( - DebugSolver::Probe(WipProbe { steps, .. }) - | DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { - evaluation: WipProbe { steps, .. }, - .. - }), - DebugSolver::Probe(probe), - ) => { - steps.push(WipProbeStep::CommitIfOkStart); - assert_eq!(probe.kind, None); - steps.extend(probe.steps); - steps.push(WipProbeStep::CommitIfOkSuccess); - } - _ => unreachable!(), - } - } - } - pub fn new_evaluate_added_goals(&mut self) -> ProofTreeBuilder<'tcx> { self.nested(|| WipAddedGoalsEvaluation { evaluations: vec![], result: None }) } diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index b1c03e82cab9b..5b45e1a34e485 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -177,7 +177,7 @@ impl<'tcx> FallibleTypeFolder> for NormalizationFolder<'_, 'tcx> { fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result, Self::Error> { let infcx = self.at.infcx; debug_assert_eq!(ty, infcx.shallow_resolve(ty)); - if !ty.has_projections() { + if !ty.has_aliases() { return Ok(ty); } @@ -204,7 +204,7 @@ impl<'tcx> FallibleTypeFolder> for NormalizationFolder<'_, 'tcx> { fn try_fold_const(&mut self, ct: ty::Const<'tcx>) -> Result, Self::Error> { let infcx = self.at.infcx; debug_assert_eq!(ct, infcx.shallow_resolve(ct)); - if !ct.has_projections() { + if !ct.has_aliases() { return Ok(ct); } diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs index 6668889323512..fb296d55100de 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs @@ -1,4 +1,4 @@ -use crate::traits::{check_args_compatible, specialization_graph}; +use crate::traits::specialization_graph; use super::assembly::structural_traits::AsyncCallableRelevantTypes; use super::assembly::{self, structural_traits, Candidate}; @@ -7,6 +7,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::LangItem; use rustc_infer::traits::query::NoSolution; +use rustc_infer::traits::solve::inspect::ProbeKind; use rustc_infer::traits::specialization_graph::LeafDef; use rustc_infer::traits::Reveal; use rustc_middle::traits::solve::{ @@ -30,14 +31,41 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { &mut self, goal: Goal<'tcx, NormalizesTo<'tcx>>, ) -> QueryResult<'tcx> { - let def_id = goal.predicate.def_id(); - let def_kind = self.tcx().def_kind(def_id); - match def_kind { - DefKind::OpaqueTy => return self.normalize_opaque_type(goal), - _ => self.set_is_normalizes_to_goal(), + self.set_is_normalizes_to_goal(); + debug_assert!(self.term_is_fully_unconstrained(goal)); + let normalize_result = self + .probe(|&result| ProbeKind::TryNormalizeNonRigid { result }) + .enter(|this| this.normalize_at_least_one_step(goal)); + + match normalize_result { + Ok(res) => Ok(res), + Err(NoSolution) => { + let Goal { param_env, predicate: NormalizesTo { alias, term } } = goal; + if alias.opt_kind(self.tcx()).is_some() { + self.relate_rigid_alias_non_alias( + param_env, + alias, + ty::Variance::Invariant, + term, + )?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else { + // FIXME(generic_const_exprs): we currently do not support rigid + // unevaluated constants. + Err(NoSolution) + } + } } + } - debug_assert!(self.term_is_fully_unconstrained(goal)); + /// Normalize the given alias by at least one step. If the alias is rigid, this + /// returns `NoSolution`. + #[instrument(level = "debug", skip(self), ret)] + fn normalize_at_least_one_step( + &mut self, + goal: Goal<'tcx, NormalizesTo<'tcx>>, + ) -> QueryResult<'tcx> { + let def_id = goal.predicate.def_id(); match self.tcx().def_kind(def_id) { DefKind::AssocTy | DefKind::AssocConst => { match self.tcx().associated_item(def_id).container { @@ -52,35 +80,22 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } DefKind::AnonConst => self.normalize_anon_const(goal), DefKind::TyAlias => self.normalize_weak_type(goal), + DefKind::OpaqueTy => self.normalize_opaque_type(goal), kind => bug!("unknown DefKind {} in normalizes-to goal: {goal:#?}", kind.descr(def_id)), } } - /// When normalizing an associated item, constrain the result to `term`. - /// - /// While `NormalizesTo` goals have the normalized-to term as an argument, - /// this argument is always fully unconstrained for associated items. - /// It is therefore appropriate to instead think of these `NormalizesTo` goals - /// as function returning a term after normalizing. - /// - /// When equating an inference variable and an alias, we tend to emit `alias-relate` - /// goals and only actually instantiate the inference variable with an alias if the - /// alias is rigid. However, this means that constraining the expected term of - /// such goals ends up fully structurally normalizing the resulting type instead of - /// only by one step. To avoid this we instead use structural equality here, resulting - /// in each `NormalizesTo` only projects by a single step. + /// When normalizing an associated item, constrain the expected term to `term`. /// - /// Not doing so, currently causes issues because trying to normalize an opaque type - /// during alias-relate doesn't actually constrain the opaque if the concrete type - /// is an inference variable. This means that `NormalizesTo` for associated types - /// normalizing to an opaque type always resulted in ambiguity, breaking tests e.g. - /// tests/ui/type-alias-impl-trait/issue-78450.rs. + /// We know `term` to always be a fully unconstrained inference variable, so + /// `eq` should never fail here. However, in case `term` contains aliases, we + /// emit nested `AliasRelate` goals to structurally normalize the alias. pub fn instantiate_normalizes_to_term( &mut self, goal: Goal<'tcx, NormalizesTo<'tcx>>, term: ty::Term<'tcx>, ) { - self.eq_structurally_relating_aliases(goal.param_env, goal.predicate.term, term) + self.eq(goal.param_env, goal.predicate.term, term) .expect("expected goal term to be fully unconstrained"); } } @@ -247,7 +262,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { assoc_def.defining_node, ); - if !check_args_compatible(tcx, assoc_def.item, args) { + if !tcx.check_args_compatible(assoc_def.item.def_id, args) { return error_response( ecx, "associated item has mismatched generic item arguments", diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs index 356c3776c04ce..9fdb280cdc6da 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs @@ -58,12 +58,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } - let expected = self.structurally_normalize_ty(goal.param_env, expected)?; - if expected.is_ty_var() { - return self - .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); - } - // Otherwise, define a new opaque type self.insert_hidden_type(opaque_type_key, goal.param_env, expected)?; self.add_item_bounds_for_hidden_type( diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index c909a0b49e24e..73e94da165fb0 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -6,13 +6,13 @@ use super::*; use crate::errors::UnableToConstructConstantValue; use crate::infer::region_constraints::{Constraint, RegionConstraintData}; use crate::traits::project::ProjectAndUnifyResult; + +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry}; +use rustc_data_structures::unord::UnordSet; use rustc_infer::infer::DefineOpaqueTypes; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::{Region, RegionVid}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; - -use std::collections::hash_map::Entry; use std::collections::VecDeque; use std::iter; @@ -25,8 +25,8 @@ pub enum RegionTarget<'tcx> { #[derive(Default, Debug, Clone)] pub struct RegionDeps<'tcx> { - larger: FxIndexSet>, - smaller: FxIndexSet>, + pub larger: FxIndexSet>, + pub smaller: FxIndexSet>, } pub enum AutoTraitResult { @@ -35,17 +35,10 @@ pub enum AutoTraitResult { NegativeImpl, } -#[allow(dead_code)] -impl AutoTraitResult { - fn is_auto(&self) -> bool { - matches!(self, AutoTraitResult::PositiveImpl(_) | AutoTraitResult::NegativeImpl) - } -} - pub struct AutoTraitInfo<'cx> { pub full_user_env: ty::ParamEnv<'cx>, pub region_data: RegionConstraintData<'cx>, - pub vid_to_region: FxHashMap>, + pub vid_to_region: FxIndexMap>, } pub struct AutoTraitFinder<'tcx> { @@ -88,19 +81,12 @@ impl<'tcx> AutoTraitFinder<'tcx> { let infcx = tcx.infer_ctxt().build(); let mut selcx = SelectionContext::new(&infcx); - for polarity in [true, false] { + for polarity in [ty::PredicatePolarity::Positive, ty::PredicatePolarity::Negative] { let result = selcx.select(&Obligation::new( tcx, ObligationCause::dummy(), orig_env, - ty::TraitPredicate { - trait_ref, - polarity: if polarity { - ty::PredicatePolarity::Positive - } else { - ty::PredicatePolarity::Negative - }, - }, + ty::TraitPredicate { trait_ref, polarity }, )); if let Ok(Some(ImplSource::UserDefined(_))) = result { debug!( @@ -114,7 +100,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { } let infcx = tcx.infer_ctxt().build(); - let mut fresh_preds = FxHashSet::default(); + let mut fresh_preds = FxIndexSet::default(); // Due to the way projections are handled by SelectionContext, we need to run // evaluate_predicates twice: once on the original param env, and once on the result of @@ -239,7 +225,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { ty: Ty<'tcx>, param_env: ty::ParamEnv<'tcx>, user_env: ty::ParamEnv<'tcx>, - fresh_preds: &mut FxHashSet>, + fresh_preds: &mut FxIndexSet>, ) -> Option<(ty::ParamEnv<'tcx>, ty::ParamEnv<'tcx>)> { let tcx = infcx.tcx; @@ -252,7 +238,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { let mut select = SelectionContext::new(infcx); - let mut already_visited = FxHashSet::default(); + let mut already_visited = UnordSet::new(); let mut predicates = VecDeque::new(); predicates.push_back(ty::Binder::dummy(ty::TraitPredicate { trait_ref: ty::TraitRef::new(infcx.tcx, trait_did, [ty]), @@ -473,9 +459,9 @@ impl<'tcx> AutoTraitFinder<'tcx> { fn map_vid_to_region<'cx>( &self, regions: &RegionConstraintData<'cx>, - ) -> FxHashMap> { - let mut vid_map: FxHashMap, RegionDeps<'cx>> = FxHashMap::default(); - let mut finished_map = FxHashMap::default(); + ) -> FxIndexMap> { + let mut vid_map = FxIndexMap::, RegionDeps<'cx>>::default(); + let mut finished_map = FxIndexMap::default(); for (constraint, _) in ®ions.constraints { match constraint { @@ -513,25 +499,22 @@ impl<'tcx> AutoTraitFinder<'tcx> { } while !vid_map.is_empty() { - #[allow(rustc::potential_query_instability)] - let target = *vid_map.keys().next().expect("Keys somehow empty"); - let deps = vid_map.remove(&target).expect("Entry somehow missing"); + let target = *vid_map.keys().next().unwrap(); + let deps = vid_map.swap_remove(&target).unwrap(); for smaller in deps.smaller.iter() { for larger in deps.larger.iter() { match (smaller, larger) { (&RegionTarget::Region(_), &RegionTarget::Region(_)) => { - if let Entry::Occupied(v) = vid_map.entry(*smaller) { + if let IndexEntry::Occupied(v) = vid_map.entry(*smaller) { let smaller_deps = v.into_mut(); smaller_deps.larger.insert(*larger); - // FIXME(#120456) - is `swap_remove` correct? smaller_deps.larger.swap_remove(&target); } - if let Entry::Occupied(v) = vid_map.entry(*larger) { + if let IndexEntry::Occupied(v) = vid_map.entry(*larger) { let larger_deps = v.into_mut(); larger_deps.smaller.insert(*smaller); - // FIXME(#120456) - is `swap_remove` correct? larger_deps.smaller.swap_remove(&target); } } @@ -542,17 +525,15 @@ impl<'tcx> AutoTraitFinder<'tcx> { // Do nothing; we don't care about regions that are smaller than vids. } (&RegionTarget::RegionVid(_), &RegionTarget::RegionVid(_)) => { - if let Entry::Occupied(v) = vid_map.entry(*smaller) { + if let IndexEntry::Occupied(v) = vid_map.entry(*smaller) { let smaller_deps = v.into_mut(); smaller_deps.larger.insert(*larger); - // FIXME(#120456) - is `swap_remove` correct? smaller_deps.larger.swap_remove(&target); } - if let Entry::Occupied(v) = vid_map.entry(*larger) { + if let IndexEntry::Occupied(v) = vid_map.entry(*larger) { let larger_deps = v.into_mut(); larger_deps.smaller.insert(*smaller); - // FIXME(#120456) - is `swap_remove` correct? larger_deps.smaller.swap_remove(&target); } } @@ -560,6 +541,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { } } } + finished_map } @@ -588,7 +570,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { ty: Ty<'_>, nested: impl Iterator>, computed_preds: &mut FxIndexSet>, - fresh_preds: &mut FxHashSet>, + fresh_preds: &mut FxIndexSet>, predicates: &mut VecDeque>, selcx: &mut SelectionContext<'_, 'tcx>, ) -> bool { diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 2712ba1945156..8625ad378f789 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -210,7 +210,7 @@ fn overlap<'tcx>( .intercrate(true) .with_next_trait_solver(tcx.next_trait_solver_in_coherence()) .build(); - let selcx = &mut SelectionContext::with_treat_inductive_cycle_as_ambig(&infcx); + let selcx = &mut SelectionContext::new(&infcx); if track_ambiguity_causes.is_yes() { selcx.enable_tracking_intercrate_ambiguity_causes(); } @@ -477,7 +477,8 @@ fn plug_infer_with_placeholders<'tcx>( if ty.is_ty_var() { let Ok(InferOk { value: (), obligations }) = self.infcx.at(&ObligationCause::dummy(), ty::ParamEnv::empty()).eq( - DefineOpaqueTypes::No, + // Comparing against a type variable never registers hidden types anyway + DefineOpaqueTypes::Yes, ty, Ty::new_placeholder( self.infcx.tcx, @@ -504,7 +505,9 @@ fn plug_infer_with_placeholders<'tcx>( if ct.is_ct_infer() { let Ok(InferOk { value: (), obligations }) = self.infcx.at(&ObligationCause::dummy(), ty::ParamEnv::empty()).eq( - DefineOpaqueTypes::No, + // The types of the constants are the same, so there is no hidden type + // registration happening anyway. + DefineOpaqueTypes::Yes, ct, ty::Const::new_placeholder( self.infcx.tcx, @@ -532,7 +535,8 @@ fn plug_infer_with_placeholders<'tcx>( if r.is_var() { let Ok(InferOk { value: (), obligations }) = self.infcx.at(&ObligationCause::dummy(), ty::ParamEnv::empty()).eq( - DefineOpaqueTypes::No, + // Lifetimes don't contain opaque types (or any types for that matter). + DefineOpaqueTypes::Yes, r, ty::Region::new_placeholder( self.infcx.tcx, @@ -554,11 +558,7 @@ fn plug_infer_with_placeholders<'tcx>( } } - value.visit_with(&mut PlugInferWithPlaceholder { - infcx, - universe, - var: ty::BoundVar::from_u32(0), - }); + value.visit_with(&mut PlugInferWithPlaceholder { infcx, universe, var: ty::BoundVar::ZERO }); } fn try_prove_negated_where_clause<'tcx>( diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 28f4f81e7d869..af90372b97ce3 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -3842,7 +3842,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { self.probe(|_| { match self .at(&ObligationCause::misc(expr.span, body_id), param_env) - .eq(DefineOpaqueTypes::No, expected, actual) + // Doesn't actually matter if we define opaque types here, this is just used for + // diagnostics, and the result is never kept around. + .eq(DefineOpaqueTypes::Yes, expected, actual) { Ok(_) => (), // We ignore nested obligations here for now. Err(err) => type_diffs.push(err), @@ -4931,7 +4933,7 @@ fn point_at_assoc_type_restriction( let hir::WherePredicate::BoundPredicate(pred) = pred else { continue; }; - let mut bounds = pred.bounds.iter().peekable(); + let mut bounds = pred.bounds.iter(); while let Some(bound) = bounds.next() { let Some(trait_ref) = bound.trait_ref() else { continue; diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index 7a62030353d96..aef98dbad5fe2 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -1128,10 +1128,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { err: &mut Diag<'_>, ) -> bool { let span = obligation.cause.span; - struct V { + /// Look for the (direct) sub-expr of `?`, and return it if it's a `.` method call. + struct FindMethodSubexprOfTry { search_span: Span, } - impl<'v> Visitor<'v> for V { + impl<'v> Visitor<'v> for FindMethodSubexprOfTry { type Result = ControlFlow<&'v hir::Expr<'v>>; fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) -> Self::Result { if let hir::ExprKind::Match(expr, _arms, hir::MatchSource::TryDesugar(_)) = ex.kind @@ -1149,8 +1150,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. }) => body_id, _ => return false, }; - let ControlFlow::Break(expr) = - (V { search_span: span }).visit_body(self.tcx.hir().body(*body_id)) + let ControlFlow::Break(expr) = (FindMethodSubexprOfTry { search_span: span }) + .visit_body(self.tcx.hir().body(*body_id)) else { return false; }; diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 34c891d400e45..a04a3bc6ebedf 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -72,7 +72,7 @@ pub struct PendingPredicateObligation<'tcx> { } // `PendingPredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] static_assert_size!(PendingPredicateObligation<'_>, 72); impl<'tcx> FulfillmentContext<'tcx> { @@ -311,7 +311,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { let infcx = self.selcx.infcx; - if obligation.predicate.has_projections() { + if obligation.predicate.has_aliases() { let mut obligations = Vec::new(); let predicate = normalize_with_depth_to( &mut self.selcx, @@ -429,7 +429,8 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { // as the cause of an overflow. ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { match self.selcx.infcx.at(&obligation.cause, obligation.param_env).eq( - DefineOpaqueTypes::No, + // Only really excercised by generic_const_exprs + DefineOpaqueTypes::Yes, ct.ty(), ty, ) { @@ -571,7 +572,9 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { if let Ok(new_obligations) = infcx .at(&obligation.cause, obligation.param_env) .trace(c1, c2) - .eq(DefineOpaqueTypes::No, a.args, b.args) + // Can define opaque types as this is only reachable with + // `generic_const_exprs` + .eq(DefineOpaqueTypes::Yes, a.args, b.args) { return ProcessResult::Changed(mk_pending( new_obligations.into_obligations(), @@ -582,7 +585,9 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { (_, _) => { if let Ok(new_obligations) = infcx .at(&obligation.cause, obligation.param_env) - .eq(DefineOpaqueTypes::No, c1, c2) + // Can define opaque types as this is only reachable with + // `generic_const_exprs` + .eq(DefineOpaqueTypes::Yes, c1, c2) { return ProcessResult::Changed(mk_pending( new_obligations.into_obligations(), @@ -623,7 +628,9 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { match (evaluate(c1), evaluate(c2)) { (Ok(c1), Ok(c2)) => { match self.selcx.infcx.at(&obligation.cause, obligation.param_env).eq( - DefineOpaqueTypes::No, + // Can define opaque types as this is only reachable with + // `generic_const_exprs` + DefineOpaqueTypes::Yes, c1, c2, ) { diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index c875d3da47e5f..2c8116b779b45 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -61,12 +61,12 @@ pub use self::specialize::{ pub use self::structural_match::search_for_structural_match_violation; pub use self::structural_normalize::StructurallyNormalizeExt; pub use self::util::elaborate; +pub use self::util::{expand_trait_aliases, TraitAliasExpander, TraitAliasExpansionInfo}; +pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices}; pub use self::util::{ - check_args_compatible, supertrait_def_ids, supertraits, transitive_bounds, - transitive_bounds_that_define_assoc_item, SupertraitDefIds, + supertrait_def_ids, supertraits, transitive_bounds, transitive_bounds_that_define_assoc_item, + SupertraitDefIds, }; -pub use self::util::{expand_trait_aliases, TraitAliasExpander}; -pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices}; pub use self::util::{with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer}; pub use rustc_infer::traits::*; diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index 15bfffef3cede..b4969926f6423 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -101,6 +101,8 @@ pub(super) fn needs_normalization<'tcx, T: TypeVisitable>>( value: &T, reveal: Reveal, ) -> bool { + // This mirrors `ty::TypeFlags::HAS_ALIASES` except that we take `Reveal` into account. + let mut flags = ty::TypeFlags::HAS_TY_PROJECTION | ty::TypeFlags::HAS_TY_WEAK | ty::TypeFlags::HAS_TY_INHERENT diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 12371155303f9..9246a41a2bcd4 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -2,7 +2,6 @@ use std::ops::ControlFlow; -use super::check_args_compatible; use super::specialization_graph; use super::translate_args; use super::util; @@ -432,7 +431,7 @@ pub(super) fn opt_normalize_projection_type<'a, 'b, 'tcx>( let projected_term = selcx.infcx.resolve_vars_if_possible(projected_term); - let mut result = if projected_term.has_projections() { + let mut result = if projected_term.has_aliases() { let normalized_ty = normalize_with_depth_to( selcx, param_env, @@ -508,7 +507,7 @@ pub(super) fn opt_normalize_projection_type<'a, 'b, 'tcx>( /// because it contains `[type error]`. Yuck! (See issue #29857 for /// one case where this arose.) fn normalize_to_error<'a, 'tcx>( - selcx: &mut SelectionContext<'a, 'tcx>, + selcx: &SelectionContext<'a, 'tcx>, param_env: ty::ParamEnv<'tcx>, projection_ty: ty::AliasTy<'tcx>, cause: ObligationCause<'tcx>, @@ -596,7 +595,7 @@ pub fn normalize_inherent_projection<'a, 'b, 'tcx>( let ty = tcx.type_of(alias_ty.def_id).instantiate(tcx, args); let mut ty = selcx.infcx.resolve_vars_if_possible(ty); - if ty.has_projections() { + if ty.has_aliases() { ty = normalize_with_depth_to(selcx, param_env, cause.clone(), depth + 1, ty, obligations); } @@ -2030,7 +2029,7 @@ fn confirm_impl_candidate<'cx, 'tcx>( } else { ty.map_bound(|ty| ty.into()) }; - if !check_args_compatible(tcx, assoc_ty.item, args) { + if !tcx.check_args_compatible(assoc_ty.item.def_id, args) { let err = Ty::new_error_with_message( tcx, obligation.cause.span, diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs index 3b33f6e6144a1..279d96dec728c 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs @@ -15,7 +15,7 @@ where type QueryResponse = T; fn try_fast_path(_tcx: TyCtxt<'tcx>, key: &ParamEnvAnd<'tcx, Self>) -> Option { - if !key.value.value.has_projections() { Some(key.value.value) } else { None } + if !key.value.value.has_aliases() { Some(key.value.value) } else { None } } fn perform_query( diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 6f512a1173f5d..0459246553b0b 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -28,7 +28,7 @@ use crate::traits::{ BuiltinDerivedObligation, ImplDerivedObligation, ImplDerivedObligationCause, ImplSource, ImplSourceUserDefinedData, Normalized, Obligation, ObligationCause, PolyTraitObligation, PredicateObligation, Selection, SelectionError, SignatureMismatch, TraitNotObjectSafe, - Unimplemented, + TraitObligation, Unimplemented, }; use super::BuiltinImplConditions; @@ -678,17 +678,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn_host_effect: ty::Const<'tcx>, ) -> Result>, SelectionError<'tcx>> { debug!(?obligation, "confirm_fn_pointer_candidate"); + let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); let tcx = self.tcx(); - - let Some(self_ty) = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars()) else { - // FIXME: Ideally we'd support `for<'a> fn(&'a ()): Fn(&'a ())`, - // but we do not currently. Luckily, such a bound is not - // particularly useful, so we don't expect users to write - // them often. - return Err(SelectionError::Unimplemented); - }; - let sig = self_ty.fn_sig(tcx); let trait_ref = closure_trait_ref_and_return_type( tcx, @@ -700,7 +693,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) .map_bound(|(trait_ref, _)| trait_ref); - let mut nested = self.confirm_poly_trait_refs(obligation, trait_ref)?; + let mut nested = + self.equate_trait_refs(obligation.with(tcx, placeholder_predicate), trait_ref)?; let cause = obligation.derived_cause(BuiltinDerivedObligation); // Confirm the `type Output: Sized;` bound that is present on `FnOnce` @@ -748,10 +742,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result>, SelectionError<'tcx>> { - // Okay to skip binder because the args on coroutine types never - // touch bound regions, they just capture the in-scope - // type/region parameters. - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else { bug!("closure candidate for non-closure {:?}", obligation); }; @@ -760,15 +752,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let coroutine_sig = args.as_coroutine().sig(); - // NOTE: The self-type is a coroutine type and hence is - // in fact unparameterized (or at least does not reference any - // regions bound in the obligation). - let self_ty = obligation - .predicate - .self_ty() - .no_bound_vars() - .expect("unboxed closure type should not capture bound vars from the predicate"); - let (trait_ref, _, _) = super::util::coroutine_trait_ref_and_outputs( self.tcx(), obligation.predicate.def_id(), @@ -776,7 +759,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { coroutine_sig, ); - let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?; + let nested = self.equate_trait_refs( + obligation.with(self.tcx(), placeholder_predicate), + ty::Binder::dummy(trait_ref), + )?; debug!(?trait_ref, ?nested, "coroutine candidate obligations"); Ok(nested) @@ -786,10 +772,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result>, SelectionError<'tcx>> { - // Okay to skip binder because the args on coroutine types never - // touch bound regions, they just capture the in-scope - // type/region parameters. - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else { bug!("closure candidate for non-closure {:?}", obligation); }; @@ -801,11 +785,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let (trait_ref, _) = super::util::future_trait_ref_and_outputs( self.tcx(), obligation.predicate.def_id(), - obligation.predicate.no_bound_vars().expect("future has no bound vars").self_ty(), + self_ty, coroutine_sig, ); - let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?; + let nested = self.equate_trait_refs( + obligation.with(self.tcx(), placeholder_predicate), + ty::Binder::dummy(trait_ref), + )?; debug!(?trait_ref, ?nested, "future candidate obligations"); Ok(nested) @@ -815,10 +802,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result>, SelectionError<'tcx>> { - // Okay to skip binder because the args on coroutine types never - // touch bound regions, they just capture the in-scope - // type/region parameters. - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else { bug!("closure candidate for non-closure {:?}", obligation); }; @@ -830,11 +815,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let (trait_ref, _) = super::util::iterator_trait_ref_and_outputs( self.tcx(), obligation.predicate.def_id(), - obligation.predicate.no_bound_vars().expect("iterator has no bound vars").self_ty(), + self_ty, gen_sig, ); - let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?; + let nested = self.equate_trait_refs( + obligation.with(self.tcx(), placeholder_predicate), + ty::Binder::dummy(trait_ref), + )?; debug!(?trait_ref, ?nested, "iterator candidate obligations"); Ok(nested) @@ -844,10 +832,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result>, SelectionError<'tcx>> { - // Okay to skip binder because the args on coroutine types never - // touch bound regions, they just capture the in-scope - // type/region parameters. - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else { bug!("closure candidate for non-closure {:?}", obligation); }; @@ -859,11 +845,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let (trait_ref, _) = super::util::async_iterator_trait_ref_and_outputs( self.tcx(), obligation.predicate.def_id(), - obligation.predicate.no_bound_vars().expect("iterator has no bound vars").self_ty(), + self_ty, gen_sig, ); - let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?; + let nested = self.equate_trait_refs( + obligation.with(self.tcx(), placeholder_predicate), + ty::Binder::dummy(trait_ref), + )?; debug!(?trait_ref, ?nested, "iterator candidate obligations"); Ok(nested) @@ -874,14 +863,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result>, SelectionError<'tcx>> { - // Okay to skip binder because the args on closure types never - // touch bound regions, they just capture the in-scope - // type/region parameters. - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let self_ty: Ty<'_> = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); + let trait_ref = match *self_ty.kind() { - ty::Closure(_, args) => { - self.closure_trait_ref_unnormalized(obligation, args, self.tcx().consts.true_) - } + ty::Closure(..) => self.closure_trait_ref_unnormalized( + self_ty, + obligation.predicate.def_id(), + self.tcx().consts.true_, + ), ty::CoroutineClosure(_, args) => { args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| { ty::TraitRef::new( @@ -896,7 +886,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } }; - self.confirm_poly_trait_refs(obligation, trait_ref) + self.equate_trait_refs(obligation.with(self.tcx(), placeholder_predicate), trait_ref) } #[instrument(skip(self), level = "debug")] @@ -904,8 +894,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result>, SelectionError<'tcx>> { + let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); + let tcx = self.tcx(); - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); let mut nested = vec![]; let (trait_ref, kind_ty) = match *self_ty.kind() { @@ -972,7 +964,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { _ => bug!("expected callable type for AsyncFn candidate"), }; - nested.extend(self.confirm_poly_trait_refs(obligation, trait_ref)?); + nested.extend( + self.equate_trait_refs(obligation.with(tcx, placeholder_predicate), trait_ref)?, + ); let goal_kind = self.tcx().async_fn_trait_kind_from_def_id(obligation.predicate.def_id()).unwrap(); @@ -1025,34 +1019,32 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// selection of the impl. Therefore, if there is a mismatch, we /// report an error to the user. #[instrument(skip(self), level = "trace")] - fn confirm_poly_trait_refs( + fn equate_trait_refs( &mut self, - obligation: &PolyTraitObligation<'tcx>, - self_ty_trait_ref: ty::PolyTraitRef<'tcx>, + obligation: TraitObligation<'tcx>, + found_trait_ref: ty::PolyTraitRef<'tcx>, ) -> Result>, SelectionError<'tcx>> { - let obligation_trait_ref = - self.infcx.enter_forall_and_leak_universe(obligation.predicate.to_poly_trait_ref()); - let self_ty_trait_ref = self.infcx.instantiate_binder_with_fresh_vars( + let found_trait_ref = self.infcx.instantiate_binder_with_fresh_vars( obligation.cause.span, HigherRankedType, - self_ty_trait_ref, + found_trait_ref, ); // Normalize the obligation and expected trait refs together, because why not - let Normalized { obligations: nested, value: (obligation_trait_ref, expected_trait_ref) } = + let Normalized { obligations: nested, value: (obligation_trait_ref, found_trait_ref) } = ensure_sufficient_stack(|| { normalize_with_depth( self, obligation.param_env, obligation.cause.clone(), obligation.recursion_depth + 1, - (obligation_trait_ref, self_ty_trait_ref), + (obligation.predicate.trait_ref, found_trait_ref), ) }); // needed to define opaque types for tests/ui/type-alias-impl-trait/assoc-projection-ice.rs self.infcx .at(&obligation.cause, obligation.param_env) - .eq(DefineOpaqueTypes::Yes, obligation_trait_ref, expected_trait_ref) + .eq(DefineOpaqueTypes::Yes, obligation_trait_ref, found_trait_ref) .map(|InferOk { mut obligations, .. }| { obligations.extend(nested); obligations @@ -1060,7 +1052,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .map_err(|terr| { SignatureMismatch(Box::new(SignatureMismatchData { expected_trait_ref: ty::Binder::dummy(obligation_trait_ref), - found_trait_ref: ty::Binder::dummy(expected_trait_ref), + found_trait_ref: ty::Binder::dummy(found_trait_ref), terr, })) }) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 1894fbba30270..aa4ab9c7ee9c6 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -60,6 +60,20 @@ use rustc_middle::ty::print::with_no_trimmed_paths; mod candidate_assembly; mod confirmation; +/// Whether to consider the binder of higher ranked goals for the `leak_check` when +/// evaluating higher-ranked goals. See #119820 for more info. +/// +/// While this is a bit hacky, it is necessary to match the behavior of the new solver: +/// We eagerly instantiate binders in the new solver, outside of candidate selection, so +/// the leak check inside of candidates does not consider any bound vars from the higher +/// ranked goal. However, we do exit the binder once we're completely finished with a goal, +/// so the leak-check can be used in evaluate by causing nested higher-ranked goals to fail. +#[derive(Debug, Copy, Clone)] +enum LeakCheckHigherRankedGoal { + No, + Yes, +} + #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub enum IntercrateAmbiguityCause<'tcx> { DownstreamCrate { trait_ref: ty::TraitRef<'tcx>, self_ty: Option> }, @@ -126,8 +140,6 @@ pub struct SelectionContext<'cx, 'tcx> { /// policy. In essence, canonicalized queries need their errors propagated /// rather than immediately reported because we do not have accurate spans. query_mode: TraitQueryMode, - - treat_inductive_cycle: TreatInductiveCycleAs, } // A stack that walks back up the stack frame. @@ -208,27 +220,6 @@ enum BuiltinImplConditions<'tcx> { Ambiguous, } -#[derive(Copy, Clone)] -pub enum TreatInductiveCycleAs { - /// This is the previous behavior, where `Recur` represents an inductive - /// cycle that is known not to hold. This is not forwards-compatible with - /// coinduction, and will be deprecated. This is the default behavior - /// of the old trait solver due to back-compat reasons. - Recur, - /// This is the behavior of the new trait solver, where inductive cycles - /// are treated as ambiguous and possibly holding. - Ambig, -} - -impl From for EvaluationResult { - fn from(treat: TreatInductiveCycleAs) -> EvaluationResult { - match treat { - TreatInductiveCycleAs::Ambig => EvaluatedToAmbigStackDependent, - TreatInductiveCycleAs::Recur => EvaluatedToErrStackDependent, - } - } -} - impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { pub fn new(infcx: &'cx InferCtxt<'tcx>) -> SelectionContext<'cx, 'tcx> { SelectionContext { @@ -236,19 +227,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { freshener: infcx.freshener(), intercrate_ambiguity_causes: None, query_mode: TraitQueryMode::Standard, - treat_inductive_cycle: TreatInductiveCycleAs::Recur, - } - } - - pub fn with_treat_inductive_cycle_as_ambig( - infcx: &'cx InferCtxt<'tcx>, - ) -> SelectionContext<'cx, 'tcx> { - // Should be executed in a context where caching is disabled, - // otherwise the cache is poisoned with the temporary result. - assert!(infcx.intercrate); - SelectionContext { - treat_inductive_cycle: TreatInductiveCycleAs::Ambig, - ..SelectionContext::new(infcx) } } @@ -420,7 +398,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let mut no_candidates_apply = true; for c in candidate_set.vec.iter() { - if self.evaluate_candidate(stack, c)?.may_apply() { + if self + .evaluate_candidate(stack, c, LeakCheckHigherRankedGoal::No)? + .may_apply() + { no_candidates_apply = false; break; } @@ -491,7 +472,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // is needed for specialization. Propagate overflow if it occurs. let mut candidates = candidates .into_iter() - .map(|c| match self.evaluate_candidate(stack, &c) { + .map(|c| match self.evaluate_candidate(stack, &c, LeakCheckHigherRankedGoal::No) { Ok(eval) if eval.may_apply() => { Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval })) } @@ -581,7 +562,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &PredicateObligation<'tcx>, ) -> Result { debug_assert!(!self.infcx.next_trait_solver()); - self.evaluation_probe(|this| { + self.evaluation_probe(|this, _outer_universe| { let goal = this.infcx.resolve_vars_if_possible((obligation.predicate, obligation.param_env)); let mut result = this.evaluate_predicate_recursively( @@ -597,13 +578,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }) } + /// Computes the evaluation result of `op`, discarding any constraints. + /// + /// This also runs for leak check to allow higher ranked region errors to impact + /// selection. By default it checks for leaks from all universes created inside of + /// `op`, but this can be overwritten if necessary. fn evaluation_probe( &mut self, - op: impl FnOnce(&mut Self) -> Result, + op: impl FnOnce(&mut Self, &mut ty::UniverseIndex) -> Result, ) -> Result { self.infcx.probe(|snapshot| -> Result { - let outer_universe = self.infcx.universe(); - let result = op(self)?; + let mut outer_universe = self.infcx.universe(); + let result = op(self, &mut outer_universe)?; match self.infcx.leak_check(outer_universe, Some(snapshot)) { Ok(()) => {} @@ -622,9 +608,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }) } - /// Evaluates the predicates in `predicates` recursively. Note that - /// this applies projections in the predicates, and therefore + /// Evaluates the predicates in `predicates` recursively. This may + /// guide inference. If this is not desired, run it inside of a /// is run within an inference probe. + /// `probe`. #[instrument(skip(self, stack), level = "debug")] fn evaluate_predicates_recursively<'o, I>( &mut self, @@ -756,7 +743,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { stack.update_reached_depth(stack_arg.1); return Ok(EvaluatedToOk); } else { - return Ok(self.treat_inductive_cycle.into()); + return Ok(EvaluatedToAmbigStackDependent); } } return Ok(EvaluatedToOk); @@ -875,7 +862,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } ProjectAndUnifyResult::FailedNormalization => Ok(EvaluatedToAmbig), - ProjectAndUnifyResult::Recursive => Ok(self.treat_inductive_cycle.into()), + ProjectAndUnifyResult::Recursive => Ok(EvaluatedToAmbigStackDependent), ProjectAndUnifyResult::MismatchedProjectionTypes(_) => Ok(EvaluatedToErr), } } @@ -919,7 +906,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .infcx .at(&obligation.cause, obligation.param_env) .trace(c1, c2) - .eq(DefineOpaqueTypes::No, a.args, b.args) + // Can define opaque types as this is only reachable with + // `generic_const_exprs` + .eq(DefineOpaqueTypes::Yes, a.args, b.args) { return self.evaluate_predicates_recursively( previous_stack, @@ -932,7 +921,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if let Ok(InferOk { obligations, value: () }) = self .infcx .at(&obligation.cause, obligation.param_env) - .eq(DefineOpaqueTypes::No, c1, c2) + // Can define opaque types as this is only reachable with + // `generic_const_exprs` + .eq(DefineOpaqueTypes::Yes, c1, c2) { return self.evaluate_predicates_recursively( previous_stack, @@ -962,7 +953,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match (evaluate(c1), evaluate(c2)) { (Ok(c1), Ok(c2)) => { match self.infcx.at(&obligation.cause, obligation.param_env).eq( - DefineOpaqueTypes::No, + // Can define opaque types as this is only reachable with + // `generic_const_exprs` + DefineOpaqueTypes::Yes, c1, c2, ) { @@ -995,7 +988,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::PredicateKind::Ambiguous => Ok(EvaluatedToAmbig), ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { match self.infcx.at(&obligation.cause, obligation.param_env).eq( - DefineOpaqueTypes::No, + // Only really excercised by generic_const_exprs + DefineOpaqueTypes::Yes, ct.ty(), ty, ) { @@ -1066,7 +1060,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // so we will try to normalize the obligation and evaluate again. // we will replace it with new solver in the future. if EvaluationResult::EvaluatedToErr == result - && fresh_trait_pred.has_projections() + && fresh_trait_pred.has_aliases() && fresh_trait_pred.is_global() { let mut nested_obligations = Vec::new(); @@ -1180,7 +1174,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Some(EvaluatedToOk) } else { debug!("evaluate_stack --> recursive, inductive"); - Some(self.treat_inductive_cycle.into()) + Some(EvaluatedToAmbigStackDependent) } } else { None @@ -1230,7 +1224,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } match self.candidate_from_obligation(stack) { - Ok(Some(c)) => self.evaluate_candidate(stack, &c), + Ok(Some(c)) => self.evaluate_candidate(stack, &c, LeakCheckHigherRankedGoal::Yes), Ok(None) => Ok(EvaluatedToAmbig), Err(Overflow(OverflowError::Canonical)) => Err(OverflowError::Canonical), Err(..) => Ok(EvaluatedToErr), @@ -1255,6 +1249,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// Further evaluates `candidate` to decide whether all type parameters match and whether nested /// obligations are met. Returns whether `candidate` remains viable after this further /// scrutiny. + /// + /// Depending on the value of [LeakCheckHigherRankedGoal], we may ignore the binder of the goal + /// when eagerly detecting higher ranked region errors via the `leak_check`. See that enum for + /// more info. #[instrument( level = "debug", skip(self, stack), @@ -1265,10 +1263,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, stack: &TraitObligationStack<'o, 'tcx>, candidate: &SelectionCandidate<'tcx>, + leak_check_higher_ranked_goal: LeakCheckHigherRankedGoal, ) -> Result { - let mut result = self.evaluation_probe(|this| { - let candidate = (*candidate).clone(); - match this.confirm_candidate(stack.obligation, candidate) { + let mut result = self.evaluation_probe(|this, outer_universe| { + // We eagerly instantiate higher ranked goals to prevent universe errors + // from impacting candidate selection. This matches the behavior of the new + // solver. This slightly weakens type inference. + // + // In case there are no unresolved type or const variables this + // should still not be necessary to select a unique impl as any overlap + // relying on a universe error from higher ranked goals should have resulted + // in an overlap error in coherence. + let p = self.infcx.enter_forall_and_leak_universe(stack.obligation.predicate); + let obligation = stack.obligation.with(this.tcx(), ty::Binder::dummy(p)); + match leak_check_higher_ranked_goal { + LeakCheckHigherRankedGoal::No => *outer_universe = self.infcx.universe(), + LeakCheckHigherRankedGoal::Yes => {} + } + + match this.confirm_candidate(&obligation, candidate.clone()) { Ok(selection) => { debug!(?selection); this.evaluate_predicates_recursively( @@ -1693,8 +1706,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { stack: &TraitObligationStack<'o, 'tcx>, where_clause_trait_ref: ty::PolyTraitRef<'tcx>, ) -> Result { - self.evaluation_probe(|this| { - match this.match_where_clause_trait_ref(stack.obligation, where_clause_trait_ref) { + self.evaluation_probe(|this, outer_universe| { + // Eagerly instantiate higher ranked goals. + // + // See the comment in `evaluate_candidate` to see why. + let p = self.infcx.enter_forall_and_leak_universe(stack.obligation.predicate); + let obligation = stack.obligation.with(this.tcx(), ty::Binder::dummy(p)); + *outer_universe = self.infcx.universe(); + match this.match_where_clause_trait_ref(&obligation, where_clause_trait_ref) { Ok(obligations) => this.evaluate_predicates_recursively(stack.list(), obligations), Err(()) => Ok(EvaluatedToErr), } @@ -2679,26 +2698,18 @@ impl<'tcx> SelectionContext<'_, 'tcx> { #[instrument(skip(self), level = "debug")] fn closure_trait_ref_unnormalized( &mut self, - obligation: &PolyTraitObligation<'tcx>, - args: GenericArgsRef<'tcx>, + self_ty: Ty<'tcx>, + fn_trait_def_id: DefId, fn_host_effect: ty::Const<'tcx>, ) -> ty::PolyTraitRef<'tcx> { + let ty::Closure(_, args) = *self_ty.kind() else { + bug!("expected closure, found {self_ty}"); + }; let closure_sig = args.as_closure().sig(); - debug!(?closure_sig); - - // NOTE: The self-type is an unboxed closure type and hence is - // in fact unparameterized (or at least does not reference any - // regions bound in the obligation). - let self_ty = obligation - .predicate - .self_ty() - .no_bound_vars() - .expect("unboxed closure type should not capture bound vars from the predicate"); - closure_trait_ref_and_return_type( self.tcx(), - obligation.predicate.def_id(), + fn_trait_def_id, self_ty, closure_sig, util::TupleArgumentsFlag::No, diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index 27dd8f26489e7..46a0a4eb5ef8f 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -239,15 +239,20 @@ fn fulfill_implication<'tcx>( let source_trait = ImplSubject::Trait(source_trait_ref); - let selcx = &mut SelectionContext::new(infcx); + let selcx = SelectionContext::new(infcx); let target_args = infcx.fresh_args_for_item(DUMMY_SP, target_impl); let (target_trait, obligations) = - util::impl_subject_and_oblig(selcx, param_env, target_impl, target_args, error_cause); + util::impl_subject_and_oblig(&selcx, param_env, target_impl, target_args, error_cause); // do the impls unify? If not, no specialization. let Ok(InferOk { obligations: more_obligations, .. }) = infcx .at(&ObligationCause::dummy(), param_env) - .eq(DefineOpaqueTypes::No, source_trait, target_trait) + // Ok to use `Yes`, as all the generic params are already replaced by inference variables, + // which will match the opaque type no matter if it is defining or not. + // Any concrete type that would match the opaque would already be handled by coherence rules, + // and thus either be ok to match here and already have errored, or it won't match, in which + // case there is no issue anyway. + .eq(DefineOpaqueTypes::Yes, source_trait, target_trait) else { debug!("fulfill_implication: {:?} does not unify with {:?}", source_trait, target_trait); return Err(()); @@ -402,10 +407,6 @@ fn report_conflicting_impls<'tcx>( impl_span: Span, err: &mut Diag<'_, G>, ) { - if (overlap.trait_ref, overlap.self_ty).references_error() { - err.downgrade_to_delayed_bug(); - } - match tcx.span_of_impl(overlap.with_impl) { Ok(span) => { err.span_label(span, "first implementation here"); @@ -458,6 +459,11 @@ fn report_conflicting_impls<'tcx>( ) }); + // Don't report overlap errors if the header references error + if let Err(err) = (overlap.trait_ref, overlap.self_ty).error_reported() { + return Err(err); + } + match used_to_be_allowed { None => { let reported = if overlap.with_impl.is_local() diff --git a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs index 1aa65b87f0b7b..dba014d58b00d 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs @@ -198,7 +198,7 @@ impl<'tcx> Children { } } -fn iter_children(children: &mut Children) -> impl Iterator + '_ { +fn iter_children(children: &Children) -> impl Iterator + '_ { let nonblanket = children.non_blanket_impls.iter().flat_map(|(_, v)| v.iter()); children.blanket_impls.iter().chain(nonblanket).cloned() } diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index 29d063321a791..d29fc7921bcbd 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -205,7 +205,7 @@ impl Iterator for SupertraitDefIds<'_> { /// returning the resulting subject and all obligations that arise. /// The obligations are closed under normalization. pub fn impl_subject_and_oblig<'a, 'tcx>( - selcx: &mut SelectionContext<'a, 'tcx>, + selcx: &SelectionContext<'a, 'tcx>, param_env: ty::ParamEnv<'tcx>, impl_def_id: DefId, impl_args: GenericArgsRef<'tcx>, @@ -344,48 +344,6 @@ pub enum TupleArgumentsFlag { No, } -// Verify that the trait item and its implementation have compatible args lists -pub fn check_args_compatible<'tcx>( - tcx: TyCtxt<'tcx>, - assoc_item: ty::AssocItem, - args: ty::GenericArgsRef<'tcx>, -) -> bool { - fn check_args_compatible_inner<'tcx>( - tcx: TyCtxt<'tcx>, - generics: &'tcx ty::Generics, - args: &'tcx [ty::GenericArg<'tcx>], - ) -> bool { - if generics.count() != args.len() { - return false; - } - - let (parent_args, own_args) = args.split_at(generics.parent_count); - - if let Some(parent) = generics.parent - && let parent_generics = tcx.generics_of(parent) - && !check_args_compatible_inner(tcx, parent_generics, parent_args) - { - return false; - } - - for (param, arg) in std::iter::zip(&generics.params, own_args) { - match (¶m.kind, arg.unpack()) { - (ty::GenericParamDefKind::Type { .. }, ty::GenericArgKind::Type(_)) - | (ty::GenericParamDefKind::Lifetime, ty::GenericArgKind::Lifetime(_)) - | (ty::GenericParamDefKind::Const { .. }, ty::GenericArgKind::Const(_)) => {} - _ => return false, - } - } - - true - } - - let generics = tcx.generics_of(assoc_item.def_id); - // Chop off any additional args (RPITIT) args - let args = &args[0..generics.count().min(args.len())]; - check_args_compatible_inner(tcx, generics, args) -} - /// Executes `f` on `value` after replacing all escaping bound variables with placeholders /// and then replaces these placeholders with the original bound variables in the result. /// diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 7941a8fe95c8e..a44a5ae0e6b42 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -647,6 +647,8 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { fn visit_ty(&mut self, t: as ty::Interner>::Ty) -> Self::Result { debug!("wf bounds for t={:?} t.kind={:#?}", t, t.kind()); + let tcx = self.tcx(); + match *t.kind() { ty::Bool | ty::Char @@ -707,6 +709,14 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { } ty::FnDef(did, args) => { + // HACK: Check the return type of function definitions for + // well-formedness to mostly fix #84533. This is still not + // perfect and there may be ways to abuse the fact that we + // ignore requirements with escaping bound vars. That's a + // more general issue however. + let fn_sig = tcx.fn_sig(did).instantiate(tcx, args); + fn_sig.output().skip_binder().visit_with(self); + let obligations = self.nominal_obligations(did, args); self.out.extend(obligations); } @@ -716,7 +726,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() { let cause = self.cause(traits::ReferenceOutlivesReferent(t)); self.out.push(traits::Obligation::with_depth( - self.tcx(), + tcx, cause, self.recursion_depth, self.param_env, @@ -805,12 +815,12 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { // obligations that don't refer to Self and // checking those - let defer_to_coercion = self.tcx().features().object_safe_for_dispatch; + let defer_to_coercion = tcx.features().object_safe_for_dispatch; if !defer_to_coercion { if let Some(principal) = data.principal_def_id() { self.out.push(traits::Obligation::with_depth( - self.tcx(), + tcx, self.cause(traits::WellFormed(None)), self.recursion_depth, self.param_env, @@ -835,7 +845,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { ty::Infer(_) => { let cause = self.cause(traits::WellFormed(None)); self.out.push(traits::Obligation::with_depth( - self.tcx(), + tcx, cause, self.recursion_depth, self.param_env, @@ -850,6 +860,8 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { } fn visit_const(&mut self, c: as ty::Interner>::Const) -> Self::Result { + let tcx = self.tcx(); + match c.kind() { ty::ConstKind::Unevaluated(uv) => { if !c.has_escaping_bound_vars() { @@ -861,7 +873,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { )); let cause = self.cause(traits::WellFormed(None)); self.out.push(traits::Obligation::with_depth( - self.tcx(), + tcx, cause, self.recursion_depth, self.param_env, @@ -873,7 +885,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { let cause = self.cause(traits::WellFormed(None)); self.out.push(traits::Obligation::with_depth( - self.tcx(), + tcx, cause, self.recursion_depth, self.param_env, @@ -895,7 +907,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { )); let cause = self.cause(traits::WellFormed(None)); self.out.push(traits::Obligation::with_depth( - self.tcx(), + tcx, cause, self.recursion_depth, self.param_env, diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index 2a2e53a81ed81..acbcc3918b2fb 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -82,7 +82,7 @@ fn check_binop(op: mir::BinOp) -> bool { match op { Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor | BitAnd | BitOr | Shl | ShlUnchecked | Shr | ShrUnchecked | Eq | Lt | Le | Ne | Ge - | Gt => true, + | Gt | Cmp => true, Offset => false, } } diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 331970ac36233..509727cdeabc9 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -8,7 +8,9 @@ use rustc_middle::ty::layout::{ IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, MAX_SIMD_LANES, }; use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, AdtDef, EarlyBinder, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{ + self, AdtDef, EarlyBinder, FieldDef, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, +}; use rustc_session::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo}; use rustc_span::sym; use rustc_span::symbol::Symbol; @@ -239,9 +241,9 @@ fn layout_of_uncached<'tcx>( // Arrays and slices. ty::Array(element, mut count) => { - if count.has_projections() { + if count.has_aliases() { count = tcx.normalize_erasing_regions(param_env, count); - if count.has_projections() { + if count.has_aliases() { return Err(error(cx, LayoutError::Unknown(ty))); } } @@ -377,7 +379,7 @@ fn layout_of_uncached<'tcx>( } // Type of the first ADT field: - let f0_ty = fields[FieldIdx::from_u32(0)].ty(tcx, args); + let f0_ty = fields[FieldIdx::ZERO].ty(tcx, args); // Heterogeneous SIMD vectors are not supported: // (should be caught by typeck) @@ -506,6 +508,40 @@ fn layout_of_uncached<'tcx>( )); } + let err_if_unsized = |field: &FieldDef, err_msg: &str| { + let field_ty = tcx.type_of(field.did); + let is_unsized = tcx + .try_instantiate_and_normalize_erasing_regions(args, cx.param_env, field_ty) + .map(|f| !f.is_sized(tcx, cx.param_env)) + .map_err(|e| { + error( + cx, + LayoutError::NormalizationFailure(field_ty.instantiate_identity(), e), + ) + })?; + + if is_unsized { + cx.tcx.dcx().span_delayed_bug(tcx.def_span(def.did()), err_msg.to_owned()); + Err(error(cx, LayoutError::Unknown(ty))) + } else { + Ok(()) + } + }; + + if def.is_struct() { + if let Some((_, fields_except_last)) = + def.non_enum_variant().fields.raw.split_last() + { + for f in fields_except_last { + err_if_unsized(f, "only the last field of a struct can be unsized")?; + } + } + } else { + for f in def.all_fields() { + err_if_unsized(f, &format!("{}s cannot have unsized fields", def.descr()))?; + } + } + let get_discriminant_type = |min, max| Integer::repr_discr(tcx, ty, &def.repr(), min, max); diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index fc16edc6d1309..a652bb781161c 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -7,7 +7,6 @@ use rustc_middle::ty::util::{CheckRegions, NotUniqueParam}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; use rustc_span::Span; -use rustc_trait_selection::traits::check_args_compatible; use crate::errors::{DuplicateArg, NotParam}; @@ -250,7 +249,7 @@ impl<'tcx> TypeVisitor> for OpaqueTypeCollector<'tcx> { ty::GenericArgs::identity_for_item(self.tcx, parent), ); - if check_args_compatible(self.tcx, assoc, impl_args) { + if self.tcx.check_args_compatible(assoc.def_id, impl_args) { self.tcx .type_of(assoc.def_id) .instantiate(self.tcx, impl_args) diff --git a/compiler/rustc_ty_utils/src/sig_types.rs b/compiler/rustc_ty_utils/src/sig_types.rs index 63654a453ddee..19c092c5ddf3f 100644 --- a/compiler/rustc_ty_utils/src/sig_types.rs +++ b/compiler/rustc_ty_utils/src/sig_types.rs @@ -13,6 +13,7 @@ pub trait SpannedTypeVisitor<'tcx> { fn visit(&mut self, span: Span, value: impl TypeVisitable>) -> Self::Result; } +#[instrument(level = "trace", skip(tcx, visitor))] pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( tcx: TyCtxt<'tcx>, item: LocalDefId, @@ -36,7 +37,7 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( for (hir, ty) in hir_sig.inputs.iter().zip(ty_sig.inputs().iter()) { try_visit!(visitor.visit(hir.span, ty.map_bound(|x| *x))); } - for (pred, span) in tcx.predicates_of(item).instantiate_identity(tcx) { + for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) { try_visit!(visitor.visit(span, pred)); } } @@ -54,7 +55,7 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( // Associated types in traits don't necessarily have a type that we can visit try_visit!(visitor.visit(ty.span, tcx.type_of(item).instantiate_identity())); } - for (pred, span) in tcx.predicates_of(item).instantiate_identity(tcx) { + for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) { try_visit!(visitor.visit(span, pred)); } } @@ -76,7 +77,7 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( let ty = field.ty(tcx, args); try_visit!(visitor.visit(span, ty)); } - for (pred, span) in tcx.predicates_of(item).instantiate_identity(tcx) { + for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) { try_visit!(visitor.visit(span, pred)); } } @@ -95,12 +96,12 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( _ => tcx.def_span(item), }; try_visit!(visitor.visit(span, tcx.type_of(item).instantiate_identity())); - for (pred, span) in tcx.predicates_of(item).instantiate_identity(tcx) { + for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) { try_visit!(visitor.visit(span, pred)); } } DefKind::TraitAlias | DefKind::Trait => { - for (pred, span) in tcx.predicates_of(item).instantiate_identity(tcx) { + for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) { try_visit!(visitor.visit(span, pred)); } } diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index cd199222d900a..997b410f819ed 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -78,8 +78,10 @@ bitflags! { /// Does this have `ConstKind::Unevaluated`? const HAS_CT_PROJECTION = 1 << 14; - /// Could this type be normalized further? - const HAS_PROJECTION = TypeFlags::HAS_TY_PROJECTION.bits() + /// Does this have `Alias` or `ConstKind::Unevaluated`? + /// + /// Rephrased, could this term be normalized further? + const HAS_ALIASES = TypeFlags::HAS_TY_PROJECTION.bits() | TypeFlags::HAS_TY_WEAK.bits() | TypeFlags::HAS_TY_OPAQUE.bits() | TypeFlags::HAS_TY_INHERENT.bits() diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index 8d402588398e4..01bb3d73dbdf3 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -136,31 +136,21 @@ pub trait TypeFolder: FallibleTypeFolder { t.super_fold_with(self) } - fn fold_ty(&mut self, t: I::Ty) -> I::Ty - where - I::Ty: TypeSuperFoldable, - { + fn fold_ty(&mut self, t: I::Ty) -> I::Ty { t.super_fold_with(self) } // The default region folder is a no-op because `Region` is non-recursive - // and has no `super_fold_with` method to call. That also explains the - // lack of `I::Region: TypeSuperFoldable` bound on this method. + // and has no `super_fold_with` method to call. fn fold_region(&mut self, r: I::Region) -> I::Region { r } - fn fold_const(&mut self, c: I::Const) -> I::Const - where - I::Const: TypeSuperFoldable, - { + fn fold_const(&mut self, c: I::Const) -> I::Const { c.super_fold_with(self) } - fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate - where - I::Predicate: TypeSuperFoldable, - { + fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate { p.super_fold_with(self) } } @@ -185,31 +175,21 @@ pub trait FallibleTypeFolder: Sized { t.try_super_fold_with(self) } - fn try_fold_ty(&mut self, t: I::Ty) -> Result - where - I::Ty: TypeSuperFoldable, - { + fn try_fold_ty(&mut self, t: I::Ty) -> Result { t.try_super_fold_with(self) } // The default region folder is a no-op because `Region` is non-recursive - // and has no `super_fold_with` method to call. That also explains the - // lack of `I::Region: TypeSuperFoldable` bound on this method. + // and has no `super_fold_with` method to call. fn try_fold_region(&mut self, r: I::Region) -> Result { Ok(r) } - fn try_fold_const(&mut self, c: I::Const) -> Result - where - I::Const: TypeSuperFoldable, - { + fn try_fold_const(&mut self, c: I::Const) -> Result { c.try_super_fold_with(self) } - fn try_fold_predicate(&mut self, p: I::Predicate) -> Result - where - I::Predicate: TypeSuperFoldable, - { + fn try_fold_predicate(&mut self, p: I::Predicate) -> Result { p.try_super_fold_with(self) } } @@ -234,10 +214,7 @@ where Ok(self.fold_binder(t)) } - fn try_fold_ty(&mut self, t: I::Ty) -> Result - where - I::Ty: TypeSuperFoldable, - { + fn try_fold_ty(&mut self, t: I::Ty) -> Result { Ok(self.fold_ty(t)) } @@ -245,17 +222,11 @@ where Ok(self.fold_region(r)) } - fn try_fold_const(&mut self, c: I::Const) -> Result - where - I::Const: TypeSuperFoldable, - { + fn try_fold_const(&mut self, c: I::Const) -> Result { Ok(self.fold_const(c)) } - fn try_fold_predicate(&mut self, p: I::Predicate) -> Result - where - I::Predicate: TypeSuperFoldable, - { + fn try_fold_predicate(&mut self, p: I::Predicate) -> Result { Ok(self.fold_predicate(p)) } } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index ae1e1902f14cd..7a2885dd3bb6b 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -2,13 +2,14 @@ use smallvec::SmallVec; use std::fmt::Debug; use std::hash::Hash; +use crate::fold::TypeSuperFoldable; use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable}; use crate::{ new, BoundVar, BoundVars, CanonicalVarInfo, ConstKind, DebugWithInfcx, RegionKind, TyKind, UniverseIndex, }; -pub trait Interner: Sized { +pub trait Interner: Sized + Copy { type DefId: Copy + Debug + Hash + Eq; type AdtDef: Copy + Debug + Hash + Eq; @@ -34,6 +35,7 @@ pub trait Interner: Sized { + Into + IntoKind> + TypeSuperVisitable + + TypeSuperFoldable + Flags + new::Ty; type Tys: Copy + Debug + Hash + Eq + IntoIterator; @@ -57,6 +59,7 @@ pub trait Interner: Sized { + IntoKind> + ConstTy + TypeSuperVisitable + + TypeSuperFoldable + Flags + new::Const; type AliasConst: Copy + DebugWithInfcx + Hash + Eq; @@ -82,7 +85,13 @@ pub trait Interner: Sized { type PlaceholderRegion: Copy + Debug + Hash + Eq + PlaceholderLike; // Predicates - type Predicate: Copy + Debug + Hash + Eq + TypeSuperVisitable + Flags; + type Predicate: Copy + + Debug + + Hash + + Eq + + TypeSuperVisitable + + TypeSuperFoldable + + Flags; type TraitPredicate: Copy + Debug + Hash + Eq; type RegionOutlivesPredicate: Copy + Debug + Hash + Eq; type TypeOutlivesPredicate: Copy + Debug + Hash + Eq; diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index c01baa58ae784..45e22b12a8b06 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -314,7 +314,7 @@ rustc_index::newtype_index! { } impl UniverseIndex { - pub const ROOT: UniverseIndex = UniverseIndex::from_u32(0); + pub const ROOT: UniverseIndex = UniverseIndex::ZERO; /// Returns the "next" universe index in order -- this new index /// is considered to extend all previous universes. This diff --git a/compiler/rustc_type_ir/src/new.rs b/compiler/rustc_type_ir/src/new.rs index e7e695e59085a..1572a641d06f0 100644 --- a/compiler/rustc_type_ir/src/new.rs +++ b/compiler/rustc_type_ir/src/new.rs @@ -6,6 +6,8 @@ pub trait Ty> { pub trait Region> { fn new_anon_bound(interner: I, debruijn: DebruijnIndex, var: BoundVar) -> Self; + + fn new_static(interner: I) -> Self; } pub trait Const> { diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index 839e75dba4cbf..d6a3f9f07494a 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -223,8 +223,8 @@ pub trait TypeVisitableExt: TypeVisitable { self.has_vars_bound_at_or_above(ty::INNERMOST) } - fn has_projections(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_PROJECTION) + fn has_aliases(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_ALIASES) } fn has_inherent_projections(&self) -> bool { diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index 7c536a3e91402..8f77a19fc0e50 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -329,6 +329,7 @@ pub enum BinOp { Ne, Ge, Gt, + Cmp, Offset, } @@ -368,6 +369,9 @@ impl BinOp { assert!(lhs_kind.is_primitive() || lhs_kind.is_raw_ptr() || lhs_kind.is_fn_ptr()); Ty::bool_ty() } + BinOp::Cmp => { + unimplemented!("Should cmp::Ordering be a RigidTy?"); + } } } } @@ -967,8 +971,9 @@ pub enum PointerCoercion { #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum CastKind { + // FIXME(smir-rename): rename this to PointerExposeProvenance PointerExposeAddress, - PointerFromExposedAddress, + PointerWithExposedProvenance, PointerCoercion(PointerCoercion), DynStar, IntToInt, diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs index 21db222095f45..3e8d186b97e20 100644 --- a/compiler/stable_mir/src/ty.rs +++ b/compiler/stable_mir/src/ty.rs @@ -654,7 +654,7 @@ impl AdtDef { with(|cx| cx.def_ty(self.0)) } - /// Retrieve the type of this Adt instantiating the type with the given arguments. + /// Retrieve the type of this Adt by instantiating and normalizing it with the given arguments. /// /// This will assume the type can be instantiated with these arguments. pub fn ty_with_args(&self, args: &GenericArgs) -> Ty { @@ -733,7 +733,7 @@ pub struct FieldDef { } impl FieldDef { - /// Retrieve the type of this field instantiating the type with the given arguments. + /// Retrieve the type of this field instantiating and normalizing it with the given arguments. /// /// This will assume the type can be instantiated with these arguments. pub fn ty_with_args(&self, args: &GenericArgs) -> Ty { diff --git a/library/alloc/benches/vec_deque_append.rs b/library/alloc/benches/vec_deque_append.rs index 5825bdc355f2d..30b6e600e5adc 100644 --- a/library/alloc/benches/vec_deque_append.rs +++ b/library/alloc/benches/vec_deque_append.rs @@ -5,6 +5,11 @@ const WARMUP_N: usize = 100; const BENCH_N: usize = 1000; fn main() { + if cfg!(miri) { + // Don't benchmark Miri... + // (Due to bootstrap quirks, this gets picked up by `x.py miri library/alloc --no-doc`.) + return; + } let a: VecDeque = (0..VECDEQUE_LEN).collect(); let b: VecDeque = (0..VECDEQUE_LEN).collect(); diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 41adc2e79dc74..e2fc320f280a5 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -2087,7 +2087,7 @@ impl VecDeque { // buffer without it being full emerge debug_assert!(self.is_full()); let old_cap = self.capacity(); - self.buf.reserve_for_push(old_cap); + self.buf.grow_one(); unsafe { self.handle_capacity_increase(old_cap); } @@ -2464,8 +2464,10 @@ impl VecDeque { /// /// let mut deque: VecDeque<_> = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55].into(); /// let num = 42; - /// let idx = deque.partition_point(|&x| x < num); - /// // The above is equivalent to `let idx = deque.binary_search(&num).unwrap_or_else(|x| x);` + /// let idx = deque.partition_point(|&x| x <= num); + /// // If `num` is unique, `s.partition_point(|&x| x < num)` (with `<`) is equivalent to + /// // `s.binary_search(&num).unwrap_or_else(|x| x)`, but using `<=` may allow `insert` + /// // to shift less elements. /// deque.insert(idx, num); /// assert_eq!(deque, &[0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]); /// ``` diff --git a/library/alloc/src/lib.miri.rs b/library/alloc/src/lib.miri.rs new file mode 100644 index 0000000000000..89d7f49f55d2e --- /dev/null +++ b/library/alloc/src/lib.miri.rs @@ -0,0 +1,4 @@ +//! Grep bootstrap for `MIRI_REPLACE_LIBRS_IF_NOT_TEST` to learn what this is about. +#![no_std] +extern crate alloc as realalloc; +pub use realalloc::*; diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 20b7d0afa87e4..cafd59cb0d954 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -166,7 +166,6 @@ #![feature(try_trait_v2)] #![feature(try_with_capacity)] #![feature(tuple_trait)] -#![feature(unchecked_math)] #![feature(unicode_internals)] #![feature(unsize)] #![feature(utf8_chunks)] @@ -176,6 +175,7 @@ // Language features: // tidy-alphabetical-start #![cfg_attr(bootstrap, feature(associated_type_bounds))] +#![cfg_attr(not(bootstrap), rustc_preserve_ub_checks)] #![cfg_attr(not(test), feature(coroutine_trait))] #![cfg_attr(test, feature(panic_update_hook))] #![cfg_attr(test, feature(test))] @@ -198,7 +198,6 @@ #![feature(multiple_supertrait_upcastable)] #![feature(negative_impls)] #![feature(never_type)] -#![feature(pointer_is_aligned)] #![feature(rustc_allow_const_fn_unstable)] #![feature(rustc_attrs)] #![feature(slice_internals)] diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index 175e23b543cc8..0883080d7357f 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -345,12 +345,12 @@ impl RawVec { } } - /// A specialized version of `reserve()` used only by the hot and - /// oft-instantiated `Vec::push()`, which does its own capacity check. + /// A specialized version of `self.reserve(len, 1)` which requires the + /// caller to ensure `len == self.capacity()`. #[cfg(not(no_global_oom_handling))] #[inline(never)] - pub fn reserve_for_push(&mut self, len: usize) { - if let Err(err) = self.grow_amortized(len, 1) { + pub fn grow_one(&mut self) { + if let Err(err) = self.grow_amortized(self.cap.0, 1) { handle_error(err); } } diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 7464df268cc1e..5d552c8f15c60 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -72,9 +72,9 @@ use crate::vec::Vec; /// A UTF-8–encoded, growable string. /// -/// The `String` type is the most common string type that has ownership over the -/// contents of the string. It has a close relationship with its borrowed -/// counterpart, the primitive [`str`]. +/// `String` is the most common string type. It has ownership over the contents +/// of the string, stored in a heap-allocated buffer (see [Representation](#representation)). +/// It is closely related to its borrowed counterpart, the primitive [`str`]. /// /// # Examples /// diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 8ca8046dac587..7e3463bc082a1 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1547,7 +1547,7 @@ impl Vec { // space for the new element if len == self.buf.capacity() { - self.reserve(1); + self.buf.grow_one(); } unsafe { @@ -1967,7 +1967,7 @@ impl Vec { // This will panic or abort if we would allocate > isize::MAX bytes // or if the length increment would overflow for zero-sized types. if self.len == self.buf.capacity() { - self.buf.reserve_for_push(self.len); + self.buf.grow_one(); } unsafe { let end = self.as_mut_ptr().add(self.len); diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index 04709af5c0a05..a34bce66496d8 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -37,7 +37,7 @@ #![feature(const_trait_impl)] #![feature(const_str_from_utf8)] #![feature(panic_update_hook)] -#![feature(pointer_is_aligned)] +#![feature(pointer_is_aligned_to)] #![feature(slice_flatten)] #![feature(thin_box)] #![feature(strict_provenance)] diff --git a/library/core/src/any.rs b/library/core/src/any.rs index a4252d0c9e03c..37cb8e7d303af 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -40,10 +40,10 @@ //! //! ## Examples //! -//! Consider a situation where we want to log out a value passed to a function. -//! We know the value we're working on implements Debug, but we don't know its +//! Consider a situation where we want to log a value passed to a function. +//! We know the value we're working on implements `Debug`, but we don't know its //! concrete type. We want to give special treatment to certain types: in this -//! case printing out the length of String values prior to their value. +//! case printing out the length of `String` values prior to their value. //! We don't know the concrete type of our value at compile time, so we need to //! use runtime reflection instead. //! @@ -51,7 +51,7 @@ //! use std::fmt::Debug; //! use std::any::Any; //! -//! // Logger function for any type that implements Debug. +//! // Logger function for any type that implements `Debug`. //! fn log(value: &T) { //! let value_any = value as &dyn Any; //! diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs index 12bca0b438cc3..a860c7c6aaadc 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -42,7 +42,7 @@ use crate::ascii; use crate::error::Error; use crate::escape; use crate::fmt::{self, Write}; -use crate::iter::FusedIterator; +use crate::iter::{FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce}; use crate::num::NonZero; pub(crate) use self::methods::EscapeDebugExtArgs; @@ -373,176 +373,229 @@ impl fmt::Display for EscapeDebug { } } -/// Returns an iterator that yields the lowercase equivalent of a `char`. -/// -/// This `struct` is created by the [`to_lowercase`] method on [`char`]. See -/// its documentation for more. -/// -/// [`to_lowercase`]: char::to_lowercase -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug, Clone)] -pub struct ToLowercase(CaseMappingIter); +macro_rules! casemappingiter_impls { + ($(#[$attr:meta])* $ITER_NAME:ident) => { + $(#[$attr])* + #[stable(feature = "rust1", since = "1.0.0")] + #[derive(Debug, Clone)] + pub struct $ITER_NAME(CaseMappingIter); + + #[stable(feature = "rust1", since = "1.0.0")] + impl Iterator for $ITER_NAME { + type Item = char; + fn next(&mut self) -> Option { + self.0.next() + } -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for ToLowercase { - type Item = char; - fn next(&mut self) -> Option { - self.0.next() - } - fn size_hint(&self) -> (usize, Option) { - self.0.size_hint() - } -} + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } -#[stable(feature = "case_mapping_double_ended", since = "1.59.0")] -impl DoubleEndedIterator for ToLowercase { - fn next_back(&mut self) -> Option { - self.0.next_back() - } -} + fn fold(self, init: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.0.fold(init, fold) + } -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for ToLowercase {} + fn count(self) -> usize { + self.0.count() + } -#[stable(feature = "exact_size_case_mapping_iter", since = "1.35.0")] -impl ExactSizeIterator for ToLowercase {} + fn last(self) -> Option { + self.0.last() + } -/// Returns an iterator that yields the uppercase equivalent of a `char`. -/// -/// This `struct` is created by the [`to_uppercase`] method on [`char`]. See -/// its documentation for more. -/// -/// [`to_uppercase`]: char::to_uppercase -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug, Clone)] -pub struct ToUppercase(CaseMappingIter); + fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { + self.0.advance_by(n) + } -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for ToUppercase { - type Item = char; - fn next(&mut self) -> Option { - self.0.next() - } - fn size_hint(&self) -> (usize, Option) { - self.0.size_hint() - } -} + unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item { + // SAFETY: just forwarding requirements to caller + unsafe { self.0.__iterator_get_unchecked(idx) } + } + } -#[stable(feature = "case_mapping_double_ended", since = "1.59.0")] -impl DoubleEndedIterator for ToUppercase { - fn next_back(&mut self) -> Option { - self.0.next_back() + #[stable(feature = "case_mapping_double_ended", since = "1.59.0")] + impl DoubleEndedIterator for $ITER_NAME { + fn next_back(&mut self) -> Option { + self.0.next_back() + } + + fn rfold(self, init: Acc, rfold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.0.rfold(init, rfold) + } + + fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { + self.0.advance_back_by(n) + } + } + + #[stable(feature = "fused", since = "1.26.0")] + impl FusedIterator for $ITER_NAME {} + + #[stable(feature = "exact_size_case_mapping_iter", since = "1.35.0")] + impl ExactSizeIterator for $ITER_NAME { + fn len(&self) -> usize { + self.0.len() + } + + fn is_empty(&self) -> bool { + self.0.is_empty() + } + } + + // SAFETY: forwards to inner `array::IntoIter` + #[unstable(feature = "trusted_len", issue = "37572")] + unsafe impl TrustedLen for $ITER_NAME {} + + // SAFETY: forwards to inner `array::IntoIter` + #[doc(hidden)] + #[unstable(feature = "std_internals", issue = "none")] + unsafe impl TrustedRandomAccessNoCoerce for $ITER_NAME { + const MAY_HAVE_SIDE_EFFECT: bool = false; + } + + // SAFETY: this iter has no subtypes/supertypes + #[doc(hidden)] + #[unstable(feature = "std_internals", issue = "none")] + unsafe impl TrustedRandomAccess for $ITER_NAME {} + + #[stable(feature = "char_struct_display", since = "1.16.0")] + impl fmt::Display for $ITER_NAME { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } + } } } -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for ToUppercase {} +casemappingiter_impls! { + /// Returns an iterator that yields the lowercase equivalent of a `char`. + /// + /// This `struct` is created by the [`to_lowercase`] method on [`char`]. See + /// its documentation for more. + /// + /// [`to_lowercase`]: char::to_lowercase + ToLowercase +} -#[stable(feature = "exact_size_case_mapping_iter", since = "1.35.0")] -impl ExactSizeIterator for ToUppercase {} +casemappingiter_impls! { + /// Returns an iterator that yields the uppercase equivalent of a `char`. + /// + /// This `struct` is created by the [`to_uppercase`] method on [`char`]. See + /// its documentation for more. + /// + /// [`to_uppercase`]: char::to_uppercase + ToUppercase +} #[derive(Debug, Clone)] -enum CaseMappingIter { - Three(char, char, char), - Two(char, char), - One(char), - Zero, -} +struct CaseMappingIter(core::array::IntoIter); impl CaseMappingIter { + #[inline] fn new(chars: [char; 3]) -> CaseMappingIter { + let mut iter = chars.into_iter(); if chars[2] == '\0' { + iter.next_back(); if chars[1] == '\0' { - CaseMappingIter::One(chars[0]) // Including if chars[0] == '\0' - } else { - CaseMappingIter::Two(chars[0], chars[1]) + iter.next_back(); + + // Deliberately don't check `chars[0]`, + // as '\0' lowercases to itself } - } else { - CaseMappingIter::Three(chars[0], chars[1], chars[2]) } + CaseMappingIter(iter) } } impl Iterator for CaseMappingIter { type Item = char; + fn next(&mut self) -> Option { - match *self { - CaseMappingIter::Three(a, b, c) => { - *self = CaseMappingIter::Two(b, c); - Some(a) - } - CaseMappingIter::Two(b, c) => { - *self = CaseMappingIter::One(c); - Some(b) - } - CaseMappingIter::One(c) => { - *self = CaseMappingIter::Zero; - Some(c) - } - CaseMappingIter::Zero => None, - } + self.0.next() } fn size_hint(&self) -> (usize, Option) { - let size = match self { - CaseMappingIter::Three(..) => 3, - CaseMappingIter::Two(..) => 2, - CaseMappingIter::One(_) => 1, - CaseMappingIter::Zero => 0, - }; - (size, Some(size)) + self.0.size_hint() + } + + fn fold(self, init: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.0.fold(init, fold) + } + + fn count(self) -> usize { + self.0.count() + } + + fn last(self) -> Option { + self.0.last() + } + + fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { + self.0.advance_by(n) + } + + unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item { + // SAFETY: just forwarding requirements to caller + unsafe { self.0.__iterator_get_unchecked(idx) } } } impl DoubleEndedIterator for CaseMappingIter { fn next_back(&mut self) -> Option { - match *self { - CaseMappingIter::Three(a, b, c) => { - *self = CaseMappingIter::Two(a, b); - Some(c) - } - CaseMappingIter::Two(b, c) => { - *self = CaseMappingIter::One(b); - Some(c) - } - CaseMappingIter::One(c) => { - *self = CaseMappingIter::Zero; - Some(c) - } - CaseMappingIter::Zero => None, - } + self.0.next_back() } -} -impl fmt::Display for CaseMappingIter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - CaseMappingIter::Three(a, b, c) => { - f.write_char(a)?; - f.write_char(b)?; - f.write_char(c) - } - CaseMappingIter::Two(b, c) => { - f.write_char(b)?; - f.write_char(c) - } - CaseMappingIter::One(c) => f.write_char(c), - CaseMappingIter::Zero => Ok(()), - } + fn rfold(self, init: Acc, rfold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.0.rfold(init, rfold) + } + + fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { + self.0.advance_back_by(n) } } -#[stable(feature = "char_struct_display", since = "1.16.0")] -impl fmt::Display for ToLowercase { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.0, f) +impl ExactSizeIterator for CaseMappingIter { + fn len(&self) -> usize { + self.0.len() + } + + fn is_empty(&self) -> bool { + self.0.is_empty() } } -#[stable(feature = "char_struct_display", since = "1.16.0")] -impl fmt::Display for ToUppercase { +impl FusedIterator for CaseMappingIter {} + +// SAFETY: forwards to inner `array::IntoIter` +unsafe impl TrustedLen for CaseMappingIter {} + +// SAFETY: forwards to inner `array::IntoIter` +unsafe impl TrustedRandomAccessNoCoerce for CaseMappingIter { + const MAY_HAVE_SIDE_EFFECT: bool = false; +} + +// SAFETY: `CaseMappingIter` has no subtypes/supertypes +unsafe impl TrustedRandomAccess for CaseMappingIter {} + +impl fmt::Display for CaseMappingIter { + #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.0, f) + for c in self.0.clone() { + f.write_char(c)?; + } + Ok(()) } } diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index ba86334f9505c..44ae72bcd26bf 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -231,6 +231,9 @@ mod impls { bool char } + #[cfg(not(bootstrap))] + impl_clone! { f16 f128 } + #[unstable(feature = "never_type", issue = "35121")] impl Clone for ! { #[inline] diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index a2f07814726ac..81bba927554a4 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -376,6 +376,10 @@ pub struct AssertParamIsEq { /// ``` #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] #[stable(feature = "rust1", since = "1.0.0")] +// This is a lang item only so that `BinOp::Cmp` in MIR can return it. +// It has no special behaviour, but does require that the three variants +// `Less`/`Equal`/`Greater` remain `-1_i8`/`0_i8`/`+1_i8` respectively. +#[cfg_attr(not(bootstrap), lang = "Ordering")] #[repr(i8)] pub enum Ordering { /// An ordering where a compared value is less than another. @@ -848,6 +852,7 @@ pub trait Ord: Eq + PartialOrd { #[stable(feature = "ord_max_min", since = "1.21.0")] #[inline] #[must_use] + #[cfg_attr(not(bootstrap), rustc_diagnostic_item = "cmp_ord_max")] fn max(self, other: Self) -> Self where Self: Sized, @@ -868,6 +873,7 @@ pub trait Ord: Eq + PartialOrd { #[stable(feature = "ord_max_min", since = "1.21.0")] #[inline] #[must_use] + #[cfg_attr(not(bootstrap), rustc_diagnostic_item = "cmp_ord_min")] fn min(self, other: Self) -> Self where Self: Sized, @@ -1154,6 +1160,7 @@ pub trait PartialOrd: PartialEq { /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(bootstrap), rustc_diagnostic_item = "cmp_partialord_cmp")] fn partial_cmp(&self, other: &Rhs) -> Option; /// This method tests less than (for `self` and `other`) and is used by the `<` operator. @@ -1168,6 +1175,7 @@ pub trait PartialOrd: PartialEq { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(bootstrap), rustc_diagnostic_item = "cmp_partialord_lt")] fn lt(&self, other: &Rhs) -> bool { matches!(self.partial_cmp(other), Some(Less)) } @@ -1185,6 +1193,7 @@ pub trait PartialOrd: PartialEq { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(bootstrap), rustc_diagnostic_item = "cmp_partialord_le")] fn le(&self, other: &Rhs) -> bool { matches!(self.partial_cmp(other), Some(Less | Equal)) } @@ -1201,6 +1210,7 @@ pub trait PartialOrd: PartialEq { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(bootstrap), rustc_diagnostic_item = "cmp_partialord_gt")] fn gt(&self, other: &Rhs) -> bool { matches!(self.partial_cmp(other), Some(Greater)) } @@ -1218,6 +1228,7 @@ pub trait PartialOrd: PartialEq { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(bootstrap), rustc_diagnostic_item = "cmp_partialord_ge")] fn ge(&self, other: &Rhs) -> bool { matches!(self.partial_cmp(other), Some(Greater | Equal)) } @@ -1489,6 +1500,9 @@ mod impls { bool char usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } + #[cfg(not(bootstrap))] + partial_eq_impl! { f16 f128 } + macro_rules! eq_impl { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] @@ -1541,13 +1555,23 @@ mod impls { partial_ord_impl! { f32 f64 } + #[cfg(not(bootstrap))] + partial_ord_impl! { f16 f128 } + macro_rules! ord_impl { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] impl PartialOrd for $t { #[inline] fn partial_cmp(&self, other: &$t) -> Option { - Some(self.cmp(other)) + #[cfg(bootstrap)] + { + Some(self.cmp(other)) + } + #[cfg(not(bootstrap))] + { + Some(crate::intrinsics::three_way_compare(*self, *other)) + } } #[inline(always)] fn lt(&self, other: &$t) -> bool { (*self) < (*other) } @@ -1563,11 +1587,18 @@ mod impls { impl Ord for $t { #[inline] fn cmp(&self, other: &$t) -> Ordering { - // The order here is important to generate more optimal assembly. - // See for more info. - if *self < *other { Less } - else if *self == *other { Equal } - else { Greater } + #[cfg(bootstrap)] + { + // The order here is important to generate more optimal assembly. + // See for more info. + if *self < *other { Less } + else if *self == *other { Equal } + else { Greater } + } + #[cfg(not(bootstrap))] + { + crate::intrinsics::three_way_compare(*self, *other) + } } } )*) diff --git a/library/core/src/default.rs b/library/core/src/default.rs index a507555468282..e717a8d022fa6 100644 --- a/library/core/src/default.rs +++ b/library/core/src/default.rs @@ -178,5 +178,9 @@ default_impl! { i32, 0, "Returns the default value of `0`" } default_impl! { i64, 0, "Returns the default value of `0`" } default_impl! { i128, 0, "Returns the default value of `0`" } +#[cfg(not(bootstrap))] +default_impl! { f16, 0.0f16, "Returns the default value of `0.0`" } default_impl! { f32, 0.0f32, "Returns the default value of `0.0`" } default_impl! { f64, 0.0f64, "Returns the default value of `0.0`" } +#[cfg(not(bootstrap))] +default_impl! { f128, 0.0f128, "Returns the default value of `0.0`" } diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index e880d5758ec0a..287f6c23c899f 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -2438,8 +2438,8 @@ impl Display for char { #[stable(feature = "rust1", since = "1.0.0")] impl Pointer for *const T { fn fmt(&self, f: &mut Formatter<'_>) -> Result { - // Cast is needed here because `.expose_addr()` requires `T: Sized`. - pointer_fmt_inner((*self as *const ()).expose_addr(), f) + // Cast is needed here because `.expose_provenance()` requires `T: Sized`. + pointer_fmt_inner((*self as *const ()).expose_provenance(), f) } } diff --git a/library/core/src/hash/mod.rs b/library/core/src/hash/mod.rs index ef0793a3e4652..1c93a7b28fd35 100644 --- a/library/core/src/hash/mod.rs +++ b/library/core/src/hash/mod.rs @@ -752,6 +752,18 @@ pub trait BuildHasher { #[stable(since = "1.7.0", feature = "build_hasher")] pub struct BuildHasherDefault(marker::PhantomData H>); +impl BuildHasherDefault { + /// Creates a new BuildHasherDefault for Hasher `H`. + #[unstable( + feature = "build_hasher_default_const_new", + issue = "123197", + reason = "recently added" + )] + pub const fn new() -> Self { + BuildHasherDefault(marker::PhantomData) + } +} + #[stable(since = "1.9.0", feature = "core_impl_debug")] impl fmt::Debug for BuildHasherDefault { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -778,7 +790,7 @@ impl Clone for BuildHasherDefault { #[stable(since = "1.7.0", feature = "build_hasher")] impl Default for BuildHasherDefault { fn default() -> BuildHasherDefault { - BuildHasherDefault(marker::PhantomData) + Self::new() } } diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index bb1debfc77dec..b09d9fab8a763 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2146,6 +2146,18 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn bitreverse(x: T) -> T; + /// Does a three-way comparison between the two integer arguments. + /// + /// This is included as an intrinsic as it's useful to let it be one thing + /// in MIR, rather than the multiple checks and switches that make its IR + /// large and difficult to optimize. + /// + /// The stabilized version of this intrinsic is [`Ord::cmp`]. + #[cfg(not(bootstrap))] + #[rustc_const_unstable(feature = "const_three_way_compare", issue = "none")] + #[rustc_safe_intrinsic] + pub fn three_way_compare(lhs: T, rhs: T) -> crate::cmp::Ordering; + /// Performs checked integer addition. /// /// Note that, unlike most intrinsics, this is safe to call; @@ -2224,40 +2236,45 @@ extern "rust-intrinsic" { /// Safe wrappers for this intrinsic are available on the integer /// primitives via the `checked_shl` method. For example, /// [`u32::checked_shl`] + #[cfg(not(bootstrap))] #[rustc_const_stable(feature = "const_int_unchecked", since = "1.40.0")] #[rustc_nounwind] - pub fn unchecked_shl(x: T, y: T) -> T; + pub fn unchecked_shl(x: T, y: U) -> T; /// Performs an unchecked right shift, resulting in undefined behavior when /// `y < 0` or `y >= N`, where N is the width of T in bits. /// /// Safe wrappers for this intrinsic are available on the integer /// primitives via the `checked_shr` method. For example, /// [`u32::checked_shr`] + #[cfg(not(bootstrap))] #[rustc_const_stable(feature = "const_int_unchecked", since = "1.40.0")] #[rustc_nounwind] - pub fn unchecked_shr(x: T, y: T) -> T; + pub fn unchecked_shr(x: T, y: U) -> T; /// Returns the result of an unchecked addition, resulting in /// undefined behavior when `x + y > T::MAX` or `x + y < T::MIN`. /// - /// This intrinsic does not have a stable counterpart. - #[rustc_const_unstable(feature = "const_int_unchecked_arith", issue = "none")] + /// The stable counterpart of this intrinsic is `unchecked_add` on the various + /// integer types, such as [`u16::unchecked_add`] and [`i64::unchecked_add`]. + #[rustc_const_stable(feature = "unchecked_math", since = "CURRENT_RUSTC_VERSION")] #[rustc_nounwind] pub fn unchecked_add(x: T, y: T) -> T; /// Returns the result of an unchecked subtraction, resulting in /// undefined behavior when `x - y > T::MAX` or `x - y < T::MIN`. /// - /// This intrinsic does not have a stable counterpart. - #[rustc_const_unstable(feature = "const_int_unchecked_arith", issue = "none")] + /// The stable counterpart of this intrinsic is `unchecked_sub` on the various + /// integer types, such as [`u16::unchecked_sub`] and [`i64::unchecked_sub`]. + #[rustc_const_stable(feature = "unchecked_math", since = "CURRENT_RUSTC_VERSION")] #[rustc_nounwind] pub fn unchecked_sub(x: T, y: T) -> T; /// Returns the result of an unchecked multiplication, resulting in /// undefined behavior when `x * y > T::MAX` or `x * y < T::MIN`. /// - /// This intrinsic does not have a stable counterpart. - #[rustc_const_unstable(feature = "const_int_unchecked_arith", issue = "none")] + /// The stable counterpart of this intrinsic is `unchecked_mul` on the various + /// integer types, such as [`u16::unchecked_mul`] and [`i64::unchecked_mul`]. + #[rustc_const_stable(feature = "unchecked_math", since = "CURRENT_RUSTC_VERSION")] #[rustc_nounwind] pub fn unchecked_mul(x: T, y: T) -> T; @@ -2686,12 +2703,14 @@ pub const unsafe fn typed_swap(x: *mut T, y: *mut T) { unsafe { ptr::swap_nonoverlapping(x, y, 1) }; } -/// Returns whether we should perform some UB-checking at runtime. This evaluate to the value of -/// `cfg!(debug_assertions)` during monomorphization. +/// Returns whether we should perform some UB-checking at runtime. This eventually evaluates to +/// `cfg!(debug_assertions)`, but behaves different from `cfg!` when mixing crates built with different +/// flags: if the crate has debug assertions enabled or carries the `#[rustc_preserve_ub_checks]` +/// attribute, evaluation is delayed until monomorphization (or until the call gets inlined into +/// a crate that does not delay evaluation further); otherwise it can happen any time. /// -/// This intrinsic is evaluated after monomorphization, which is relevant when mixing crates -/// compiled with and without debug_assertions. The common case here is a user program built with -/// debug_assertions linked against the distributed sysroot which is built without debug_assertions. +/// The common case here is a user program built with debug_assertions linked against the distributed +/// sysroot which is built without debug_assertions but with `#[rustc_preserve_ub_checks]`. /// For code that gets monomorphized in the user crate (i.e., generic functions and functions with /// `#[inline]`), gating assertions on `ub_checks()` rather than `cfg!(debug_assertions)` means that /// assertions are enabled whenever the *user crate* has debug assertions enabled. However if the diff --git a/library/core/src/intrinsics/simd.rs b/library/core/src/intrinsics/simd.rs index 427a95f4665da..eeff4ec609a30 100644 --- a/library/core/src/intrinsics/simd.rs +++ b/library/core/src/intrinsics/simd.rs @@ -540,6 +540,10 @@ extern "rust-intrinsic" { /// `T` must be a vector of pointers. /// /// `U` must be a vector of `usize` with the same length as `T`. + #[cfg(not(bootstrap))] + #[rustc_nounwind] + pub fn simd_expose_provenance(ptr: T) -> U; + #[cfg(bootstrap)] #[rustc_nounwind] pub fn simd_expose_addr(ptr: T) -> U; @@ -549,6 +553,10 @@ extern "rust-intrinsic" { /// /// `U` must be a vector of pointers, with the same length as `T`. #[rustc_nounwind] + #[cfg(not(bootstrap))] + pub fn simd_with_exposed_provenance(addr: T) -> U; + #[rustc_nounwind] + #[cfg(bootstrap)] pub fn simd_from_exposed_addr(addr: T) -> U; /// Swap bytes of each element. @@ -655,3 +663,8 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_flog(a: T) -> T; } + +#[cfg(bootstrap)] +pub use simd_expose_addr as simd_expose_provenance; +#[cfg(bootstrap)] +pub use simd_from_exposed_addr as simd_with_exposed_provenance; diff --git a/library/core/src/lib.miri.rs b/library/core/src/lib.miri.rs new file mode 100644 index 0000000000000..5c1027f20ba07 --- /dev/null +++ b/library/core/src/lib.miri.rs @@ -0,0 +1,4 @@ +//! Grep bootstrap for `MIRI_REPLACE_LIBRS_IF_NOT_TEST` to learn what this is about. +#![no_std] +extern crate core as realcore; +pub use realcore::*; diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 3bc1a87f848f1..ba19ca1f45d7c 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -94,6 +94,7 @@ ))] #![no_core] #![rustc_coherence_is_core] +#![cfg_attr(not(bootstrap), rustc_preserve_ub_checks)] // // Lints: #![deny(rust_2021_incompatible_or_patterns)] @@ -136,7 +137,7 @@ #![feature(const_heap)] #![feature(const_hint_assert_unchecked)] #![feature(const_index_range_slice_index)] -#![feature(const_int_unchecked_arith)] +#![feature(const_int_from_str)] #![feature(const_intrinsic_copy)] #![feature(const_intrinsic_forget)] #![feature(const_ipv4)] @@ -196,7 +197,6 @@ #![feature(str_split_inclusive_remainder)] #![feature(str_split_remainder)] #![feature(strict_provenance)] -#![feature(unchecked_math)] #![feature(unchecked_shifts)] #![feature(utf16_extra)] #![feature(utf16_extra_const)] @@ -205,6 +205,8 @@ // // Language features: // tidy-alphabetical-start +#![cfg_attr(not(bootstrap), feature(f128))] +#![cfg_attr(not(bootstrap), feature(f16))] #![feature(abi_unadjusted)] #![feature(adt_const_params)] #![feature(allow_internal_unsafe)] diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 574a357b44abb..6da05a1ca867a 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1053,7 +1053,8 @@ pub(crate) mod builtin { /// /// If the environment variable is not defined, then a compilation error /// will be emitted. To not emit a compile error, use the [`option_env!`] - /// macro instead. + /// macro instead. A compilation error will also be emitted if the + /// environment variable is not a vaild Unicode string. /// /// # Examples /// diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index a56a2578c2241..fb97b3bfa0926 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -426,7 +426,13 @@ marker_impls! { bool, char, {T: ?Sized} *const T, {T: ?Sized} *mut T, +} +#[cfg(not(bootstrap))] +marker_impls! { + #[stable(feature = "rust1", since = "1.0.0")] + Copy for + f16, f128, } #[unstable(feature = "never_type", issue = "35121")] @@ -817,6 +823,13 @@ pub trait DiscriminantKind { /// This can be used to declare that a constant with a generic type /// will not contain interior mutability, and subsequently allow /// placing the constant behind references. +/// +/// # Safety +/// +/// This trait is a core part of the language, it is just expressed as a trait in libcore for +/// convenience. Do *not* implement it for other types. +// FIXME: Eventually this trait should become `#[rustc_deny_explicit_impl]`. +// That requires porting the impls below to native internal impls. #[lang = "freeze"] #[unstable(feature = "freeze", issue = "121675")] pub unsafe auto trait Freeze {} diff --git a/library/core/src/num/error.rs b/library/core/src/num/error.rs index 14e99578a7c7d..a2d7e6f7b0754 100644 --- a/library/core/src/num/error.rs +++ b/library/core/src/num/error.rs @@ -113,8 +113,9 @@ pub enum IntErrorKind { impl ParseIntError { /// Outputs the detailed cause of parsing an integer failing. #[must_use] + #[rustc_const_unstable(feature = "const_int_from_str", issue = "59133")] #[stable(feature = "int_error_matching", since = "1.55.0")] - pub fn kind(&self) -> &IntErrorKind { + pub const fn kind(&self) -> &IntErrorKind { &self.kind } } diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index fa37ee4ffb204..e34e9b7fff644 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -60,32 +60,6 @@ macro_rules! int_impl { #[stable(feature = "int_bits_const", since = "1.53.0")] pub const BITS: u32 = <$UnsignedT>::BITS; - /// Converts a string slice in a given base to an integer. - /// - /// The string is expected to be an optional `+` or `-` sign followed by digits. - /// Leading and trailing whitespace represent an error. Digits are a subset of these characters, - /// depending on `radix`: - /// - /// * `0-9` - /// * `a-z` - /// * `A-Z` - /// - /// # Panics - /// - /// This function panics if `radix` is not in the range from 2 to 36. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - #[doc = concat!("assert_eq!(", stringify!($SelfT), "::from_str_radix(\"A\", 16), Ok(10));")] - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn from_str_radix(src: &str, radix: u32) -> Result { - from_str_radix(src, radix) - } - /// Returns the number of ones in the binary representation of `self`. /// /// # Examples @@ -492,21 +466,25 @@ macro_rules! int_impl { /// Unchecked integer addition. Computes `self + rhs`, assuming overflow /// cannot occur. /// + /// Calling `x.unchecked_add(y)` is semantically equivalent to calling + /// `x.`[`checked_add`]`(y).`[`unwrap_unchecked`]`()`. + /// + /// If you're just trying to avoid the panic in debug mode, then **do not** + /// use this. Instead, you're looking for [`wrapping_add`]. + /// /// # Safety /// /// This results in undefined behavior when #[doc = concat!("`self + rhs > ", stringify!($SelfT), "::MAX` or `self + rhs < ", stringify!($SelfT), "::MIN`,")] /// i.e. when [`checked_add`] would return `None`. /// + /// [`unwrap_unchecked`]: option/enum.Option.html#method.unwrap_unchecked #[doc = concat!("[`checked_add`]: ", stringify!($SelfT), "::checked_add")] - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "85122", - )] + #[doc = concat!("[`wrapping_add`]: ", stringify!($SelfT), "::wrapping_add")] + #[stable(feature = "unchecked_math", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "unchecked_math", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[rustc_const_unstable(feature = "unchecked_math", issue = "85122")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_add(self, rhs: Self) -> Self { @@ -630,21 +608,25 @@ macro_rules! int_impl { /// Unchecked integer subtraction. Computes `self - rhs`, assuming overflow /// cannot occur. /// + /// Calling `x.unchecked_sub(y)` is semantically equivalent to calling + /// `x.`[`checked_sub`]`(y).`[`unwrap_unchecked`]`()`. + /// + /// If you're just trying to avoid the panic in debug mode, then **do not** + /// use this. Instead, you're looking for [`wrapping_sub`]. + /// /// # Safety /// /// This results in undefined behavior when #[doc = concat!("`self - rhs > ", stringify!($SelfT), "::MAX` or `self - rhs < ", stringify!($SelfT), "::MIN`,")] /// i.e. when [`checked_sub`] would return `None`. /// + /// [`unwrap_unchecked`]: option/enum.Option.html#method.unwrap_unchecked #[doc = concat!("[`checked_sub`]: ", stringify!($SelfT), "::checked_sub")] - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "85122", - )] + #[doc = concat!("[`wrapping_sub`]: ", stringify!($SelfT), "::wrapping_sub")] + #[stable(feature = "unchecked_math", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "unchecked_math", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[rustc_const_unstable(feature = "unchecked_math", issue = "85122")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_sub(self, rhs: Self) -> Self { @@ -768,21 +750,25 @@ macro_rules! int_impl { /// Unchecked integer multiplication. Computes `self * rhs`, assuming overflow /// cannot occur. /// + /// Calling `x.unchecked_mul(y)` is semantically equivalent to calling + /// `x.`[`checked_mul`]`(y).`[`unwrap_unchecked`]`()`. + /// + /// If you're just trying to avoid the panic in debug mode, then **do not** + /// use this. Instead, you're looking for [`wrapping_mul`]. + /// /// # Safety /// /// This results in undefined behavior when #[doc = concat!("`self * rhs > ", stringify!($SelfT), "::MAX` or `self * rhs < ", stringify!($SelfT), "::MIN`,")] /// i.e. when [`checked_mul`] would return `None`. /// + /// [`unwrap_unchecked`]: option/enum.Option.html#method.unwrap_unchecked #[doc = concat!("[`checked_mul`]: ", stringify!($SelfT), "::checked_mul")] - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "85122", - )] + #[doc = concat!("[`wrapping_mul`]: ", stringify!($SelfT), "::wrapping_mul")] + #[stable(feature = "unchecked_math", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "unchecked_math", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[rustc_const_unstable(feature = "unchecked_math", issue = "85122")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self { @@ -1241,10 +1227,18 @@ macro_rules! int_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_shl`. - // Any legal shift amount is losslessly representable in the self type. - unsafe { intrinsics::unchecked_shl(self, conv_rhs_for_unchecked_shift!($SelfT, rhs)) } + #[cfg(bootstrap)] + { + // For bootstrapping, just use built-in primitive shift. + // panicking is a legal manifestation of UB + self << rhs + } + #[cfg(not(bootstrap))] + { + // SAFETY: the caller must uphold the safety contract for + // `unchecked_shl`. + unsafe { intrinsics::unchecked_shl(self, rhs) } + } } /// Checked shift right. Computes `self >> rhs`, returning `None` if `rhs` is @@ -1324,10 +1318,18 @@ macro_rules! int_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_shr`. - // Any legal shift amount is losslessly representable in the self type. - unsafe { intrinsics::unchecked_shr(self, conv_rhs_for_unchecked_shift!($SelfT, rhs)) } + #[cfg(bootstrap)] + { + // For bootstrapping, just use built-in primitive shift. + // panicking is a legal manifestation of UB + self >> rhs + } + #[cfg(not(bootstrap))] + { + // SAFETY: the caller must uphold the safety contract for + // `unchecked_shr`. + unsafe { intrinsics::unchecked_shr(self, rhs) } + } } /// Checked absolute value. Computes `self.abs()`, returning `None` if diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 03c977abbbb42..9ebbb4ffe80b5 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -6,7 +6,6 @@ use crate::ascii; use crate::hint; use crate::intrinsics; use crate::mem; -use crate::ops::{Add, Mul, Sub}; use crate::str::FromStr; // Used because the `?` operator is not allowed in a const context. @@ -286,17 +285,6 @@ macro_rules! widening_impl { }; } -macro_rules! conv_rhs_for_unchecked_shift { - ($SelfT:ty, $x:expr) => {{ - // If the `as` cast will truncate, ensure we still tell the backend - // that the pre-truncation value was also small. - if <$SelfT>::BITS < 32 { - intrinsics::assume($x <= (<$SelfT>::MAX as u32)); - } - $x as $SelfT - }}; -} - impl i8 { int_impl! { Self = i8, @@ -1386,51 +1374,19 @@ pub enum FpCategory { Normal, } -#[doc(hidden)] -trait FromStrRadixHelper: - PartialOrd + Copy + Add + Sub + Mul -{ - const MIN: Self; - fn from_u32(u: u32) -> Self; - fn checked_mul(&self, other: u32) -> Option; - fn checked_sub(&self, other: u32) -> Option; - fn checked_add(&self, other: u32) -> Option; -} - macro_rules! from_str_radix_int_impl { ($($t:ty)*) => {$( #[stable(feature = "rust1", since = "1.0.0")] impl FromStr for $t { type Err = ParseIntError; fn from_str(src: &str) -> Result { - from_str_radix(src, 10) + <$t>::from_str_radix(src, 10) } } )*} } from_str_radix_int_impl! { isize i8 i16 i32 i64 i128 usize u8 u16 u32 u64 u128 } -macro_rules! impl_helper_for { - ($($t:ty)*) => ($(impl FromStrRadixHelper for $t { - const MIN: Self = Self::MIN; - #[inline] - fn from_u32(u: u32) -> Self { u as Self } - #[inline] - fn checked_mul(&self, other: u32) -> Option { - Self::checked_mul(*self, other as Self) - } - #[inline] - fn checked_sub(&self, other: u32) -> Option { - Self::checked_sub(*self, other as Self) - } - #[inline] - fn checked_add(&self, other: u32) -> Option { - Self::checked_add(*self, other as Self) - } - })*) -} -impl_helper_for! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } - /// Determines if a string of text of that length of that radix could be guaranteed to be /// stored in the given type T. /// Note that if the radix is known to the compiler, it is just the check of digits.len that @@ -1438,92 +1394,198 @@ impl_helper_for! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } #[doc(hidden)] #[inline(always)] #[unstable(issue = "none", feature = "std_internals")] -pub fn can_not_overflow(radix: u32, is_signed_ty: bool, digits: &[u8]) -> bool { +pub const fn can_not_overflow(radix: u32, is_signed_ty: bool, digits: &[u8]) -> bool { radix <= 16 && digits.len() <= mem::size_of::() * 2 - is_signed_ty as usize } -fn from_str_radix(src: &str, radix: u32) -> Result { - use self::IntErrorKind::*; - use self::ParseIntError as PIE; +#[track_caller] +const fn from_str_radix_panic_ct(_radix: u32) -> ! { + panic!("from_str_radix_int: must lie in the range `[2, 36]`"); +} - assert!( - (2..=36).contains(&radix), - "from_str_radix_int: must lie in the range `[2, 36]` - found {}", - radix - ); +#[track_caller] +fn from_str_radix_panic_rt(radix: u32) -> ! { + panic!("from_str_radix_int: must lie in the range `[2, 36]` - found {}", radix); +} - if src.is_empty() { - return Err(PIE { kind: Empty }); +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] +#[cfg_attr(feature = "panic_immediate_abort", inline)] +#[cold] +#[track_caller] +const fn from_str_radix_assert(radix: u32) { + if 2 > radix || radix > 36 { + // The only difference between these two functions is their panic message. + intrinsics::const_eval_select((radix,), from_str_radix_panic_ct, from_str_radix_panic_rt); } +} - let is_signed_ty = T::from_u32(0) > T::MIN; - - // all valid digits are ascii, so we will just iterate over the utf8 bytes - // and cast them to chars. .to_digit() will safely return None for anything - // other than a valid ascii digit for the given radix, including the first-byte - // of multi-byte sequences - let src = src.as_bytes(); +macro_rules! from_str_radix { + ($($int_ty:ty)+) => {$( + impl $int_ty { + /// Converts a string slice in a given base to an integer. + /// + /// The string is expected to be an optional `+` sign + /// followed by digits. + /// Leading and trailing whitespace represent an error. + /// Digits are a subset of these characters, depending on `radix`: + /// + /// * `0-9` + /// * `a-z` + /// * `A-Z` + /// + /// # Panics + /// + /// This function panics if `radix` is not in the range from 2 to 36. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_str_radix(\"A\", 16), Ok(10));")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_int_from_str", issue = "59133")] + pub const fn from_str_radix(src: &str, radix: u32) -> Result<$int_ty, ParseIntError> { + use self::IntErrorKind::*; + use self::ParseIntError as PIE; + + from_str_radix_assert(radix); + + if src.is_empty() { + return Err(PIE { kind: Empty }); + } - let (is_positive, digits) = match src[0] { - b'+' | b'-' if src[1..].is_empty() => { - return Err(PIE { kind: InvalidDigit }); - } - b'+' => (true, &src[1..]), - b'-' if is_signed_ty => (false, &src[1..]), - _ => (true, src), - }; + #[allow(unused_comparisons)] + let is_signed_ty = 0 > <$int_ty>::MIN; + + // all valid digits are ascii, so we will just iterate over the utf8 bytes + // and cast them to chars. .to_digit() will safely return None for anything + // other than a valid ascii digit for the given radix, including the first-byte + // of multi-byte sequences + let src = src.as_bytes(); + + let (is_positive, mut digits) = match src { + [b'+' | b'-'] => { + return Err(PIE { kind: InvalidDigit }); + } + [b'+', rest @ ..] => (true, rest), + [b'-', rest @ ..] if is_signed_ty => (false, rest), + _ => (true, src), + }; + + let mut result = 0; + + macro_rules! unwrap_or_PIE { + ($option:expr, $kind:ident) => { + match $option { + Some(value) => value, + None => return Err(PIE { kind: $kind }), + } + }; + } - let mut result = T::from_u32(0); - - if can_not_overflow::(radix, is_signed_ty, digits) { - // If the len of the str is short compared to the range of the type - // we are parsing into, then we can be certain that an overflow will not occur. - // This bound is when `radix.pow(digits.len()) - 1 <= T::MAX` but the condition - // above is a faster (conservative) approximation of this. - // - // Consider radix 16 as it has the highest information density per digit and will thus overflow the earliest: - // `u8::MAX` is `ff` - any str of len 2 is guaranteed to not overflow. - // `i8::MAX` is `7f` - only a str of len 1 is guaranteed to not overflow. - macro_rules! run_unchecked_loop { - ($unchecked_additive_op:expr) => { - for &c in digits { - result = result * T::from_u32(radix); - let x = (c as char).to_digit(radix).ok_or(PIE { kind: InvalidDigit })?; - result = $unchecked_additive_op(result, T::from_u32(x)); + if can_not_overflow::<$int_ty>(radix, is_signed_ty, digits) { + // If the len of the str is short compared to the range of the type + // we are parsing into, then we can be certain that an overflow will not occur. + // This bound is when `radix.pow(digits.len()) - 1 <= T::MAX` but the condition + // above is a faster (conservative) approximation of this. + // + // Consider radix 16 as it has the highest information density per digit and will thus overflow the earliest: + // `u8::MAX` is `ff` - any str of len 2 is guaranteed to not overflow. + // `i8::MAX` is `7f` - only a str of len 1 is guaranteed to not overflow. + macro_rules! run_unchecked_loop { + ($unchecked_additive_op:tt) => {{ + while let [c, rest @ ..] = digits { + result = result * (radix as $int_ty); + let x = unwrap_or_PIE!((*c as char).to_digit(radix), InvalidDigit); + result = result $unchecked_additive_op (x as $int_ty); + digits = rest; + } + }}; + } + if is_positive { + run_unchecked_loop!(+) + } else { + run_unchecked_loop!(-) + }; + } else { + macro_rules! run_checked_loop { + ($checked_additive_op:ident, $overflow_err:ident) => {{ + while let [c, rest @ ..] = digits { + // When `radix` is passed in as a literal, rather than doing a slow `imul` + // the compiler can use shifts if `radix` can be expressed as a + // sum of powers of 2 (x*10 can be written as x*8 + x*2). + // When the compiler can't use these optimisations, + // the latency of the multiplication can be hidden by issuing it + // before the result is needed to improve performance on + // modern out-of-order CPU as multiplication here is slower + // than the other instructions, we can get the end result faster + // doing multiplication first and let the CPU spends other cycles + // doing other computation and get multiplication result later. + let mul = result.checked_mul(radix as $int_ty); + let x = unwrap_or_PIE!((*c as char).to_digit(radix), InvalidDigit) as $int_ty; + result = unwrap_or_PIE!(mul, $overflow_err); + result = unwrap_or_PIE!(<$int_ty>::$checked_additive_op(result, x), $overflow_err); + digits = rest; + } + }}; + } + if is_positive { + run_checked_loop!(checked_add, PosOverflow) + } else { + run_checked_loop!(checked_sub, NegOverflow) + }; } - }; + Ok(result) + } } - if is_positive { - run_unchecked_loop!(::add) - } else { - run_unchecked_loop!(::sub) - }; - } else { - macro_rules! run_checked_loop { - ($checked_additive_op:ident, $overflow_err:expr) => { - for &c in digits { - // When `radix` is passed in as a literal, rather than doing a slow `imul` - // the compiler can use shifts if `radix` can be expressed as a - // sum of powers of 2 (x*10 can be written as x*8 + x*2). - // When the compiler can't use these optimisations, - // the latency of the multiplication can be hidden by issuing it - // before the result is needed to improve performance on - // modern out-of-order CPU as multiplication here is slower - // than the other instructions, we can get the end result faster - // doing multiplication first and let the CPU spends other cycles - // doing other computation and get multiplication result later. - let mul = result.checked_mul(radix); - let x = (c as char).to_digit(radix).ok_or(PIE { kind: InvalidDigit })?; - result = mul.ok_or_else($overflow_err)?; - result = T::$checked_additive_op(&result, x).ok_or_else($overflow_err)?; - } - }; + )+} +} + +from_str_radix! { i8 u8 i16 u16 i32 u32 i64 u64 i128 u128 } + +// Re-use the relevant implementation of from_str_radix for isize and usize to avoid outputting two +// identical functions. +macro_rules! from_str_radix_size_impl { + ($($t:ident $size:ty),*) => {$( + impl $size { + /// Converts a string slice in a given base to an integer. + /// + /// The string is expected to be an optional `+` sign + /// followed by digits. + /// Leading and trailing whitespace represent an error. + /// Digits are a subset of these characters, depending on `radix`: + /// + /// * `0-9` + /// * `a-z` + /// * `A-Z` + /// + /// # Panics + /// + /// This function panics if `radix` is not in the range from 2 to 36. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(", stringify!($size), "::from_str_radix(\"A\", 16), Ok(10));")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_int_from_str", issue = "59133")] + pub const fn from_str_radix(src: &str, radix: u32) -> Result<$size, ParseIntError> { + match <$t>::from_str_radix(src, radix) { + Ok(x) => Ok(x as $size), + Err(e) => Err(e), + } } - if is_positive { - run_checked_loop!(checked_add, || PIE { kind: PosOverflow }) - } else { - run_checked_loop!(checked_sub, || PIE { kind: NegOverflow }) - }; - } - Ok(result) + })*} } + +#[cfg(target_pointer_width = "16")] +from_str_radix_size_impl! { i16 isize, u16 usize } +#[cfg(target_pointer_width = "32")] +from_str_radix_size_impl! { i32 isize, u32 usize } +#[cfg(target_pointer_width = "64")] +from_str_radix_size_impl! { i64 isize, u64 usize } diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 1171407c07a3f..62ea7abf6528a 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -11,7 +11,6 @@ use crate::ptr; use crate::str::FromStr; use crate::ub_checks; -use super::from_str_radix; use super::{IntErrorKind, ParseIntError}; /// A marker trait for primitive types which can be zero. @@ -804,7 +803,7 @@ macro_rules! nonzero_integer { impl FromStr for $Ty { type Err = ParseIntError; fn from_str(src: &str) -> Result { - Self::new(from_str_radix(src, 10)?) + Self::new(<$Int>::from_str_radix(src, 10)?) .ok_or(ParseIntError { kind: IntErrorKind::Zero }) diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 081a3c0b118ea..ba6a243041ce1 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -58,33 +58,6 @@ macro_rules! uint_impl { #[stable(feature = "int_bits_const", since = "1.53.0")] pub const BITS: u32 = Self::MAX.count_ones(); - /// Converts a string slice in a given base to an integer. - /// - /// The string is expected to be an optional `+` sign - /// followed by digits. - /// Leading and trailing whitespace represent an error. - /// Digits are a subset of these characters, depending on `radix`: - /// - /// * `0-9` - /// * `a-z` - /// * `A-Z` - /// - /// # Panics - /// - /// This function panics if `radix` is not in the range from 2 to 36. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - #[doc = concat!("assert_eq!(", stringify!($SelfT), "::from_str_radix(\"A\", 16), Ok(10));")] - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn from_str_radix(src: &str, radix: u32) -> Result { - from_str_radix(src, radix) - } - /// Returns the number of ones in the binary representation of `self`. /// /// # Examples @@ -500,21 +473,25 @@ macro_rules! uint_impl { /// Unchecked integer addition. Computes `self + rhs`, assuming overflow /// cannot occur. /// + /// Calling `x.unchecked_add(y)` is semantically equivalent to calling + /// `x.`[`checked_add`]`(y).`[`unwrap_unchecked`]`()`. + /// + /// If you're just trying to avoid the panic in debug mode, then **do not** + /// use this. Instead, you're looking for [`wrapping_add`]. + /// /// # Safety /// /// This results in undefined behavior when #[doc = concat!("`self + rhs > ", stringify!($SelfT), "::MAX` or `self + rhs < ", stringify!($SelfT), "::MIN`,")] /// i.e. when [`checked_add`] would return `None`. /// + /// [`unwrap_unchecked`]: option/enum.Option.html#method.unwrap_unchecked #[doc = concat!("[`checked_add`]: ", stringify!($SelfT), "::checked_add")] - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "85122", - )] + #[doc = concat!("[`wrapping_add`]: ", stringify!($SelfT), "::wrapping_add")] + #[stable(feature = "unchecked_math", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "unchecked_math", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[rustc_const_unstable(feature = "unchecked_math", issue = "85122")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_add(self, rhs: Self) -> Self { @@ -644,21 +621,25 @@ macro_rules! uint_impl { /// Unchecked integer subtraction. Computes `self - rhs`, assuming overflow /// cannot occur. /// + /// Calling `x.unchecked_sub(y)` is semantically equivalent to calling + /// `x.`[`checked_sub`]`(y).`[`unwrap_unchecked`]`()`. + /// + /// If you're just trying to avoid the panic in debug mode, then **do not** + /// use this. Instead, you're looking for [`wrapping_sub`]. + /// /// # Safety /// /// This results in undefined behavior when #[doc = concat!("`self - rhs > ", stringify!($SelfT), "::MAX` or `self - rhs < ", stringify!($SelfT), "::MIN`,")] /// i.e. when [`checked_sub`] would return `None`. /// + /// [`unwrap_unchecked`]: option/enum.Option.html#method.unwrap_unchecked #[doc = concat!("[`checked_sub`]: ", stringify!($SelfT), "::checked_sub")] - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "85122", - )] + #[doc = concat!("[`wrapping_sub`]: ", stringify!($SelfT), "::wrapping_sub")] + #[stable(feature = "unchecked_math", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "unchecked_math", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[rustc_const_unstable(feature = "unchecked_math", issue = "85122")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_sub(self, rhs: Self) -> Self { @@ -726,21 +707,25 @@ macro_rules! uint_impl { /// Unchecked integer multiplication. Computes `self * rhs`, assuming overflow /// cannot occur. /// + /// Calling `x.unchecked_mul(y)` is semantically equivalent to calling + /// `x.`[`checked_mul`]`(y).`[`unwrap_unchecked`]`()`. + /// + /// If you're just trying to avoid the panic in debug mode, then **do not** + /// use this. Instead, you're looking for [`wrapping_mul`]. + /// /// # Safety /// /// This results in undefined behavior when #[doc = concat!("`self * rhs > ", stringify!($SelfT), "::MAX` or `self * rhs < ", stringify!($SelfT), "::MIN`,")] /// i.e. when [`checked_mul`] would return `None`. /// + /// [`unwrap_unchecked`]: option/enum.Option.html#method.unwrap_unchecked #[doc = concat!("[`checked_mul`]: ", stringify!($SelfT), "::checked_mul")] - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "85122", - )] + #[doc = concat!("[`wrapping_mul`]: ", stringify!($SelfT), "::wrapping_mul")] + #[stable(feature = "unchecked_math", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "unchecked_math", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[rustc_const_unstable(feature = "unchecked_math", issue = "85122")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self { @@ -1301,10 +1286,18 @@ macro_rules! uint_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_shl`. - // Any legal shift amount is losslessly representable in the self type. - unsafe { intrinsics::unchecked_shl(self, conv_rhs_for_unchecked_shift!($SelfT, rhs)) } + #[cfg(bootstrap)] + { + // For bootstrapping, just use built-in primitive shift. + // panicking is a legal manifestation of UB + self << rhs + } + #[cfg(not(bootstrap))] + { + // SAFETY: the caller must uphold the safety contract for + // `unchecked_shl`. + unsafe { intrinsics::unchecked_shl(self, rhs) } + } } /// Checked shift right. Computes `self >> rhs`, returning `None` @@ -1384,10 +1377,18 @@ macro_rules! uint_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_shr`. - // Any legal shift amount is losslessly representable in the self type. - unsafe { intrinsics::unchecked_shr(self, conv_rhs_for_unchecked_shift!($SelfT, rhs)) } + #[cfg(bootstrap)] + { + // For bootstrapping, just use built-in primitive shift. + // panicking is a legal manifestation of UB + self >> rhs + } + #[cfg(not(bootstrap))] + { + // SAFETY: the caller must uphold the safety contract for + // `unchecked_shr`. + unsafe { intrinsics::unchecked_shr(self, rhs) } + } } /// Checked exponentiation. Computes `self.pow(exp)`, returning `None` if diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index a8940d9cd1eb0..cbb0a7d61db05 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -130,7 +130,7 @@ pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: boo #[cfg_attr(feature = "panic_immediate_abort", inline)] #[track_caller] #[rustc_const_unstable(feature = "panic_internals", issue = "none")] -#[lang = "panic"] // needed by codegen for panic on overflow and other `Assert` MIR terminators +#[lang = "panic"] // used by lints and miri for panics pub const fn panic(expr: &'static str) -> ! { // Use Arguments::new_const instead of format_args!("{expr}") to potentially // reduce size overhead. The format_args! macro uses str's Display trait to @@ -141,6 +141,69 @@ pub const fn panic(expr: &'static str) -> ! { panic_fmt(fmt::Arguments::new_const(&[expr])); } +// We generate functions for usage by compiler-generated assertions. +// +// Placing these functions in libcore means that all Rust programs can generate a jump into this +// code rather than expanding to panic("...") above, which adds extra bloat to call sites (for the +// constant string argument's pointer and length). +// +// This is especially important when this code is called often (e.g., with -Coverflow-checks) for +// reducing binary size impact. +macro_rules! panic_const { + ($($lang:ident = $message:expr,)+) => { + #[cfg(not(bootstrap))] + pub mod panic_const { + use super::*; + + $( + /// This is a panic called with a message that's a result of a MIR-produced Assert. + // + // never inline unless panic_immediate_abort to avoid code + // bloat at the call sites as much as possible + #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] + #[cfg_attr(feature = "panic_immediate_abort", inline)] + #[track_caller] + #[rustc_const_unstable(feature = "panic_internals", issue = "none")] + #[lang = stringify!($lang)] + pub const fn $lang() -> ! { + // Use Arguments::new_const instead of format_args!("{expr}") to potentially + // reduce size overhead. The format_args! macro uses str's Display trait to + // write expr, which calls Formatter::pad, which must accommodate string + // truncation and padding (even though none is used here). Using + // Arguments::new_const may allow the compiler to omit Formatter::pad from the + // output binary, saving up to a few kilobytes. + panic_fmt(fmt::Arguments::new_const(&[$message])); + } + )+ + } + } +} + +// Unfortunately this set of strings is replicated here and in a few places in the compiler in +// slightly different forms. It's not clear if there's a good way to deduplicate without adding +// special cases to the compiler (e.g., a const generic function wouldn't have a single definition +// shared across crates, which is exactly what we want here). +panic_const! { + panic_const_add_overflow = "attempt to add with overflow", + panic_const_sub_overflow = "attempt to subtract with overflow", + panic_const_mul_overflow = "attempt to multiply with overflow", + panic_const_div_overflow = "attempt to divide with overflow", + panic_const_rem_overflow = "attempt to calculate the remainder with overflow", + panic_const_neg_overflow = "attempt to negate with overflow", + panic_const_shr_overflow = "attempt to shift right with overflow", + panic_const_shl_overflow = "attempt to shift left with overflow", + panic_const_div_by_zero = "attempt to divide by zero", + panic_const_rem_by_zero = "attempt to calculate the remainder with a divisor of zero", + panic_const_coroutine_resumed = "coroutine resumed after completion", + panic_const_async_fn_resumed = "`async fn` resumed after completion", + panic_const_async_gen_fn_resumed = "`async gen fn` resumed after completion", + panic_const_gen_fn_none = "`gen fn` should just keep returning `None` after completion", + panic_const_coroutine_resumed_panic = "coroutine resumed after panicking", + panic_const_async_fn_resumed_panic = "`async fn` resumed after panicking", + panic_const_async_gen_fn_resumed_panic = "`async gen fn` resumed after panicking", + panic_const_gen_fn_none_panic = "`gen fn` should just keep returning `None` after panicking", +} + /// Like `panic`, but without unwinding and track_caller to reduce the impact on codesize on the caller. /// If you want `#[track_caller]` for nicer errors, call `panic_nounwind_fmt` directly. #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index a6c00ff28d427..01db050e666f2 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -136,7 +136,7 @@ impl *const T { #[unstable(feature = "ptr_to_from_bits", issue = "91126")] #[deprecated( since = "1.67.0", - note = "replaced by the `expose_addr` method, or update your code \ + note = "replaced by the `expose_provenance` method, or update your code \ to follow the strict provenance rules using its APIs" )] #[inline(always)] @@ -165,7 +165,7 @@ impl *const T { #[unstable(feature = "ptr_to_from_bits", issue = "91126")] #[deprecated( since = "1.67.0", - note = "replaced by the `ptr::from_exposed_addr` function, or update \ + note = "replaced by the `ptr::with_exposed_provenance` function, or update \ your code to follow the strict provenance rules using its APIs" )] #[allow(fuzzy_provenance_casts)] // this is an unstable and semi-deprecated cast function @@ -187,7 +187,7 @@ impl *const T { /// /// If using those APIs is not possible because there is no way to preserve a pointer with the /// required provenance, then Strict Provenance might not be for you. Use pointer-integer casts - /// or [`expose_addr`][pointer::expose_addr] and [`from_exposed_addr`][from_exposed_addr] + /// or [`expose_provenance`][pointer::expose_provenance] and [`with_exposed_provenance`][with_exposed_provenance] /// instead. However, note that this makes your code less portable and less amenable to tools /// that check for compliance with the Rust memory model. /// @@ -210,35 +210,35 @@ impl *const T { unsafe { mem::transmute(self.cast::<()>()) } } - /// Gets the "address" portion of the pointer, and 'exposes' the "provenance" part for future - /// use in [`from_exposed_addr`][]. + /// Exposes the "provenance" part of the pointer for future use in + /// [`with_exposed_provenance`][] and returns the "address" portion. /// /// This is equivalent to `self as usize`, which semantically discards *provenance* and /// *address-space* information. Furthermore, this (like the `as` cast) has the implicit /// side-effect of marking the provenance as 'exposed', so on platforms that support it you can - /// later call [`from_exposed_addr`][] to reconstitute the original pointer including its + /// later call [`with_exposed_provenance`][] to reconstitute the original pointer including its /// provenance. (Reconstructing address space information, if required, is your responsibility.) /// /// Using this method means that code is *not* following [Strict /// Provenance][super#strict-provenance] rules. Supporting - /// [`from_exposed_addr`][] complicates specification and reasoning and may not be supported by + /// [`with_exposed_provenance`][] complicates specification and reasoning and may not be supported by /// tools that help you to stay conformant with the Rust memory model, so it is recommended to /// use [`addr`][pointer::addr] wherever possible. /// /// On most platforms this will produce a value with the same bytes as the original pointer, /// because all the bytes are dedicated to describing the address. Platforms which need to store /// additional information in the pointer may not support this operation, since the 'expose' - /// side-effect which is required for [`from_exposed_addr`][] to work is typically not + /// side-effect which is required for [`with_exposed_provenance`][] to work is typically not /// available. /// /// It is unclear whether this method can be given a satisfying unambiguous specification. This /// API and its claimed semantics are part of [Exposed Provenance][super#exposed-provenance]. /// - /// [`from_exposed_addr`]: from_exposed_addr + /// [`with_exposed_provenance`]: with_exposed_provenance #[must_use] #[inline(always)] #[unstable(feature = "exposed_provenance", issue = "95228")] - pub fn expose_addr(self) -> usize { + pub fn expose_provenance(self) -> usize { // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. self.cast::<()>() as usize } @@ -1029,8 +1029,6 @@ impl *const T { #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")] - // We could always go back to wrapping if unchecked becomes unacceptable - #[rustc_allow_const_fn_unstable(const_int_unchecked_arith)] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn sub(self, count: usize) -> Self @@ -1403,8 +1401,6 @@ impl *const T { /// # Examples /// /// ``` - /// #![feature(pointer_is_aligned)] - /// /// // On some platforms, the alignment of i32 is less than 4. /// #[repr(align(4))] /// struct AlignedI32(i32); @@ -1427,7 +1423,6 @@ impl *const T { /// underlying allocation. /// /// ``` - /// #![feature(pointer_is_aligned)] /// #![feature(const_pointer_is_aligned)] /// /// // On some platforms, the alignment of primitives is less than their size. @@ -1453,7 +1448,6 @@ impl *const T { /// pointer is aligned, even if the compiletime pointer wasn't aligned. /// /// ``` - /// #![feature(pointer_is_aligned)] /// #![feature(const_pointer_is_aligned)] /// /// // On some platforms, the alignment of primitives is less than their size. @@ -1479,7 +1473,6 @@ impl *const T { /// runtime and compiletime. /// /// ``` - /// #![feature(pointer_is_aligned)] /// #![feature(const_pointer_is_aligned)] /// /// // On some platforms, the alignment of primitives is less than their size. @@ -1503,7 +1496,7 @@ impl *const T { /// [tracking issue]: https://github.com/rust-lang/rust/issues/104203 #[must_use] #[inline] - #[unstable(feature = "pointer_is_aligned", issue = "96284")] + #[stable(feature = "pointer_is_aligned", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")] pub const fn is_aligned(self) -> bool where @@ -1524,7 +1517,7 @@ impl *const T { /// # Examples /// /// ``` - /// #![feature(pointer_is_aligned)] + /// #![feature(pointer_is_aligned_to)] /// /// // On some platforms, the alignment of i32 is less than 4. /// #[repr(align(4))] @@ -1553,7 +1546,7 @@ impl *const T { /// cannot be stricter aligned than the reference's underlying allocation. /// /// ``` - /// #![feature(pointer_is_aligned)] + /// #![feature(pointer_is_aligned_to)] /// #![feature(const_pointer_is_aligned)] /// /// // On some platforms, the alignment of i32 is less than 4. @@ -1578,7 +1571,7 @@ impl *const T { /// pointer is aligned, even if the compiletime pointer wasn't aligned. /// /// ``` - /// #![feature(pointer_is_aligned)] + /// #![feature(pointer_is_aligned_to)] /// #![feature(const_pointer_is_aligned)] /// /// // On some platforms, the alignment of i32 is less than 4. @@ -1602,7 +1595,7 @@ impl *const T { /// runtime and compiletime. /// /// ``` - /// #![feature(pointer_is_aligned)] + /// #![feature(pointer_is_aligned_to)] /// #![feature(const_pointer_is_aligned)] /// /// const _: () = { @@ -1618,7 +1611,7 @@ impl *const T { /// [tracking issue]: https://github.com/rust-lang/rust/issues/104203 #[must_use] #[inline] - #[unstable(feature = "pointer_is_aligned", issue = "96284")] + #[unstable(feature = "pointer_is_aligned_to", issue = "96284")] #[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")] pub const fn is_aligned_to(self, align: usize) -> bool { if !align.is_power_of_two() { @@ -1857,6 +1850,7 @@ impl Ord for *const T { #[stable(feature = "rust1", since = "1.0.0")] impl PartialOrd for *const T { #[inline] + #[allow(ambiguous_wide_pointer_comparisons)] fn partial_cmp(&self, other: &*const T) -> Option { Some(self.cmp(other)) } diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index fe19f66a31ac4..25a06f121cdaa 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -2,6 +2,7 @@ use crate::fmt; use crate::hash::{Hash, Hasher}; +use crate::marker::Freeze; /// Provides the pointer metadata type of any pointed-to type. /// @@ -57,7 +58,7 @@ pub trait Pointee { // NOTE: Keep trait bounds in `static_assert_expected_bounds_for_metadata` // in `library/core/src/ptr/metadata.rs` // in sync with those here: - type Metadata: Copy + Send + Sync + Ord + Hash + Unpin; + type Metadata: fmt::Debug + Copy + Send + Sync + Ord + Hash + Unpin + Freeze; } /// Pointers to types implementing this trait alias are “thin”. @@ -258,6 +259,7 @@ impl PartialEq for DynMetadata { impl Ord for DynMetadata { #[inline] + #[allow(ambiguous_wide_pointer_comparisons)] fn cmp(&self, other: &Self) -> crate::cmp::Ordering { (self.vtable_ptr as *const VTable).cmp(&(other.vtable_ptr as *const VTable)) } diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 56378b437e7ee..f12ab3d50cdd4 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -340,13 +340,13 @@ //! clear where a satisfying unambiguous semantics can be defined for Exposed Provenance. //! Furthermore, Exposed Provenance will not work (well) with tools like [Miri] and [CHERI]. //! -//! Exposed Provenance is provided by the [`expose_addr`] and [`from_exposed_addr`] methods, which -//! are meant to replace `as` casts between pointers and integers. [`expose_addr`] is a lot like +//! Exposed Provenance is provided by the [`expose_provenance`] and [`with_exposed_provenance`] methods, +//! which are meant to replace `as` casts between pointers and integers. [`expose_provenance`] is a lot like //! [`addr`], but additionally adds the provenance of the pointer to a global list of 'exposed' //! provenances. (This list is purely conceptual, it exists for the purpose of specifying Rust but -//! is not materialized in actual executions, except in tools like [Miri].) [`from_exposed_addr`] +//! is not materialized in actual executions, except in tools like [Miri].) [`with_exposed_provenance`] //! can be used to construct a pointer with one of these previously 'exposed' provenances. -//! [`from_exposed_addr`] takes only `addr: usize` as arguments, so unlike in [`with_addr`] there is +//! [`with_exposed_provenance`] takes only `addr: usize` as arguments, so unlike in [`with_addr`] there is //! no indication of what the correct provenance for the returned pointer is -- and that is exactly //! what makes pointer-usize-pointer roundtrips so tricky to rigorously specify! There is no //! algorithm that decides which provenance will be used. You can think of this as "guessing" the @@ -355,10 +355,10 @@ //! there is *no* previously 'exposed' provenance that justifies the way the returned pointer will //! be used, the program has undefined behavior. //! -//! Using [`expose_addr`] or [`from_exposed_addr`] (or the `as` casts) means that code is +//! Using [`expose_provenance`] or [`with_exposed_provenance`] (or the `as` casts) means that code is //! *not* following Strict Provenance rules. The goal of the Strict Provenance experiment is to -//! determine how far one can get in Rust without the use of [`expose_addr`] and -//! [`from_exposed_addr`], and to encourage code to be written with Strict Provenance APIs only. +//! determine how far one can get in Rust without the use of [`expose_provenance`] and +//! [`with_exposed_provenance`], and to encourage code to be written with Strict Provenance APIs only. //! Maximizing the amount of such code is a major win for avoiding specification complexity and to //! facilitate adoption of tools like [CHERI] and [Miri] that can be a big help in increasing the //! confidence in (unsafe) Rust code. @@ -374,8 +374,8 @@ //! [`map_addr`]: pointer::map_addr //! [`addr`]: pointer::addr //! [`ptr::dangling`]: core::ptr::dangling -//! [`expose_addr`]: pointer::expose_addr -//! [`from_exposed_addr`]: from_exposed_addr +//! [`expose_provenance`]: pointer::expose_provenance +//! [`with_exposed_provenance`]: with_exposed_provenance //! [Miri]: https://github.com/rust-lang/miri //! [CHERI]: https://www.cl.cam.ac.uk/research/security/ctsrd/cheri/ //! [Strict Provenance]: https://github.com/rust-lang/rust/issues/95228 @@ -581,7 +581,7 @@ pub const fn null_mut() -> *mut T { /// little more than a usize address in disguise. /// /// This is different from `addr as *const T`, which creates a pointer that picks up a previously -/// exposed provenance. See [`from_exposed_addr`] for more details on that operation. +/// exposed provenance. See [`with_exposed_provenance`] for more details on that operation. /// /// This API and its claimed semantics are part of the Strict Provenance experiment, /// see the [module documentation][crate::ptr] for details. @@ -592,7 +592,7 @@ pub const fn null_mut() -> *mut T { pub const fn without_provenance(addr: usize) -> *const T { // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. // We use transmute rather than a cast so tools like Miri can tell that this - // is *not* the same as from_exposed_addr. + // is *not* the same as with_exposed_provenance. // SAFETY: every valid integer is also a valid pointer (as long as you don't dereference that // pointer). unsafe { mem::transmute(addr) } @@ -625,7 +625,7 @@ pub const fn dangling() -> *const T { /// little more than a usize address in disguise. /// /// This is different from `addr as *mut T`, which creates a pointer that picks up a previously -/// exposed provenance. See [`from_exposed_addr_mut`] for more details on that operation. +/// exposed provenance. See [`with_exposed_provenance_mut`] for more details on that operation. /// /// This API and its claimed semantics are part of the Strict Provenance experiment, /// see the [module documentation][crate::ptr] for details. @@ -636,7 +636,7 @@ pub const fn dangling() -> *const T { pub const fn without_provenance_mut(addr: usize) -> *mut T { // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. // We use transmute rather than a cast so tools like Miri can tell that this - // is *not* the same as from_exposed_addr. + // is *not* the same as with_exposed_provenance. // SAFETY: every valid integer is also a valid pointer (as long as you don't dereference that // pointer). unsafe { mem::transmute(addr) } @@ -663,7 +663,7 @@ pub const fn dangling_mut() -> *mut T { /// /// This is a more rigorously specified alternative to `addr as *const T`. The provenance of the /// returned pointer is that of *any* pointer that was previously exposed by passing it to -/// [`expose_addr`][pointer::expose_addr], or a `ptr as usize` cast. In addition, memory which is +/// [`expose_provenance`][pointer::expose_provenance], or a `ptr as usize` cast. In addition, memory which is /// outside the control of the Rust abstract machine (MMIO registers, for example) is always /// considered to be exposed, so long as this memory is disjoint from memory that will be used by /// the abstract machine such as the stack, heap, and statics. @@ -699,7 +699,7 @@ pub const fn dangling_mut() -> *mut T { #[unstable(feature = "exposed_provenance", issue = "95228")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead -pub fn from_exposed_addr(addr: usize) -> *const T +pub fn with_exposed_provenance(addr: usize) -> *const T where T: Sized, { @@ -711,7 +711,7 @@ where /// /// This is a more rigorously specified alternative to `addr as *mut T`. The provenance of the /// returned pointer is that of *any* pointer that was previously passed to -/// [`expose_addr`][pointer::expose_addr] or a `ptr as usize` cast. If there is no previously +/// [`expose_provenance`][pointer::expose_provenance] or a `ptr as usize` cast. If there is no previously /// 'exposed' provenance that justifies the way this pointer will be used, the program has undefined /// behavior. Note that there is no algorithm that decides which provenance will be used. You can /// think of this as "guessing" the right provenance, and the guess will be "maximally in your @@ -739,7 +739,7 @@ where #[unstable(feature = "exposed_provenance", issue = "95228")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead -pub fn from_exposed_addr_mut(addr: usize) -> *mut T +pub fn with_exposed_provenance_mut(addr: usize) -> *mut T where T: Sized, { @@ -1781,9 +1781,19 @@ pub(crate) const unsafe fn align_offset(p: *const T, a: usize) -> usiz // FIXME(#75598): Direct use of these intrinsics improves codegen significantly at opt-level <= // 1, where the method versions of these operations are not inlined. use intrinsics::{ - assume, cttz_nonzero, exact_div, mul_with_overflow, unchecked_rem, unchecked_shl, - unchecked_shr, unchecked_sub, wrapping_add, wrapping_mul, wrapping_sub, + assume, cttz_nonzero, exact_div, mul_with_overflow, unchecked_rem, unchecked_sub, + wrapping_add, wrapping_mul, wrapping_sub, }; + #[cfg(bootstrap)] + const unsafe fn unchecked_shl(value: usize, shift: usize) -> usize { + value << shift + } + #[cfg(bootstrap)] + const unsafe fn unchecked_shr(value: usize, shift: usize) -> usize { + value >> shift + } + #[cfg(not(bootstrap))] + use intrinsics::{unchecked_shl, unchecked_shr}; /// Calculate multiplicative modular inverse of `x` modulo `m`. /// diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 1add9ca231128..41e5ba6745827 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -142,7 +142,7 @@ impl *mut T { #[unstable(feature = "ptr_to_from_bits", issue = "91126")] #[deprecated( since = "1.67.0", - note = "replaced by the `expose_addr` method, or update your code \ + note = "replaced by the `expose_provenance` method, or update your code \ to follow the strict provenance rules using its APIs" )] #[inline(always)] @@ -171,7 +171,7 @@ impl *mut T { #[unstable(feature = "ptr_to_from_bits", issue = "91126")] #[deprecated( since = "1.67.0", - note = "replaced by the `ptr::from_exposed_addr_mut` function, or \ + note = "replaced by the `ptr::with_exposed_provenance_mut` function, or \ update your code to follow the strict provenance rules using its APIs" )] #[allow(fuzzy_provenance_casts)] // this is an unstable and semi-deprecated cast function @@ -194,7 +194,7 @@ impl *mut T { /// /// If using those APIs is not possible because there is no way to preserve a pointer with the /// required provenance, then Strict Provenance might not be for you. Use pointer-integer casts - /// or [`expose_addr`][pointer::expose_addr] and [`from_exposed_addr`][from_exposed_addr] + /// or [`expose_provenance`][pointer::expose_provenance] and [`with_exposed_provenance`][with_exposed_provenance] /// instead. However, note that this makes your code less portable and less amenable to tools /// that check for compliance with the Rust memory model. /// @@ -217,35 +217,34 @@ impl *mut T { unsafe { mem::transmute(self.cast::<()>()) } } - /// Gets the "address" portion of the pointer, and 'exposes' the "provenance" part for future - /// use in [`from_exposed_addr`][]. + /// Exposes the "provenance" part of the pointer for future use in + /// [`with_exposed_provenance`][] and returns the "address" portion. /// /// This is equivalent to `self as usize`, which semantically discards *provenance* and /// *address-space* information. Furthermore, this (like the `as` cast) has the implicit /// side-effect of marking the provenance as 'exposed', so on platforms that support it you can - /// later call [`from_exposed_addr_mut`][] to reconstitute the original pointer including its + /// later call [`with_exposed_provenance_mut`][] to reconstitute the original pointer including its /// provenance. (Reconstructing address space information, if required, is your responsibility.) /// /// Using this method means that code is *not* following [Strict /// Provenance][super#strict-provenance] rules. Supporting - /// [`from_exposed_addr_mut`][] complicates specification and reasoning and may not be supported + /// [`with_exposed_provenance_mut`][] complicates specification and reasoning and may not be supported /// by tools that help you to stay conformant with the Rust memory model, so it is recommended /// to use [`addr`][pointer::addr] wherever possible. /// /// On most platforms this will produce a value with the same bytes as the original pointer, /// because all the bytes are dedicated to describing the address. Platforms which need to store /// additional information in the pointer may not support this operation, since the 'expose' - /// side-effect which is required for [`from_exposed_addr_mut`][] to work is typically not + /// side-effect which is required for [`with_exposed_provenance_mut`][] to work is typically not /// available. /// /// It is unclear whether this method can be given a satisfying unambiguous specification. This /// API and its claimed semantics are part of [Exposed Provenance][super#exposed-provenance]. /// - /// [`from_exposed_addr_mut`]: from_exposed_addr_mut - #[must_use] + /// [`with_exposed_provenance_mut`]: with_exposed_provenance_mut #[inline(always)] #[unstable(feature = "exposed_provenance", issue = "95228")] - pub fn expose_addr(self) -> usize { + pub fn expose_provenance(self) -> usize { // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. self.cast::<()>() as usize } @@ -1119,8 +1118,6 @@ impl *mut T { #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")] - // We could always go back to wrapping if unchecked becomes unacceptable - #[rustc_allow_const_fn_unstable(const_int_unchecked_arith)] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn sub(self, count: usize) -> Self @@ -1662,8 +1659,6 @@ impl *mut T { /// # Examples /// /// ``` - /// #![feature(pointer_is_aligned)] - /// /// // On some platforms, the alignment of i32 is less than 4. /// #[repr(align(4))] /// struct AlignedI32(i32); @@ -1686,7 +1681,6 @@ impl *mut T { /// underlying allocation. /// /// ``` - /// #![feature(pointer_is_aligned)] /// #![feature(const_pointer_is_aligned)] /// #![feature(const_mut_refs)] /// @@ -1713,7 +1707,6 @@ impl *mut T { /// pointer is aligned, even if the compiletime pointer wasn't aligned. /// /// ``` - /// #![feature(pointer_is_aligned)] /// #![feature(const_pointer_is_aligned)] /// /// // On some platforms, the alignment of primitives is less than their size. @@ -1740,7 +1733,6 @@ impl *mut T { /// runtime and compiletime. /// /// ``` - /// #![feature(pointer_is_aligned)] /// #![feature(const_pointer_is_aligned)] /// /// // On some platforms, the alignment of primitives is less than their size. @@ -1764,7 +1756,7 @@ impl *mut T { /// [tracking issue]: https://github.com/rust-lang/rust/issues/104203 #[must_use] #[inline] - #[unstable(feature = "pointer_is_aligned", issue = "96284")] + #[stable(feature = "pointer_is_aligned", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")] pub const fn is_aligned(self) -> bool where @@ -1785,7 +1777,7 @@ impl *mut T { /// # Examples /// /// ``` - /// #![feature(pointer_is_aligned)] + /// #![feature(pointer_is_aligned_to)] /// /// // On some platforms, the alignment of i32 is less than 4. /// #[repr(align(4))] @@ -1814,7 +1806,7 @@ impl *mut T { /// cannot be stricter aligned than the reference's underlying allocation. /// /// ``` - /// #![feature(pointer_is_aligned)] + /// #![feature(pointer_is_aligned_to)] /// #![feature(const_pointer_is_aligned)] /// #![feature(const_mut_refs)] /// @@ -1840,7 +1832,7 @@ impl *mut T { /// pointer is aligned, even if the compiletime pointer wasn't aligned. /// /// ``` - /// #![feature(pointer_is_aligned)] + /// #![feature(pointer_is_aligned_to)] /// #![feature(const_pointer_is_aligned)] /// /// // On some platforms, the alignment of i32 is less than 4. @@ -1865,7 +1857,7 @@ impl *mut T { /// runtime and compiletime. /// /// ``` - /// #![feature(pointer_is_aligned)] + /// #![feature(pointer_is_aligned_to)] /// #![feature(const_pointer_is_aligned)] /// /// const _: () = { @@ -1881,7 +1873,7 @@ impl *mut T { /// [tracking issue]: https://github.com/rust-lang/rust/issues/104203 #[must_use] #[inline] - #[unstable(feature = "pointer_is_aligned", issue = "96284")] + #[unstable(feature = "pointer_is_aligned_to", issue = "96284")] #[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")] pub const fn is_aligned_to(self, align: usize) -> bool { if !align.is_power_of_two() { @@ -2275,6 +2267,7 @@ impl Ord for *mut T { #[stable(feature = "rust1", since = "1.0.0")] impl PartialOrd for *mut T { #[inline(always)] + #[allow(ambiguous_wide_pointer_comparisons)] fn partial_cmp(&self, other: &*mut T) -> Option { Some(self.cmp(other)) } diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index e9488917acc14..f0e4b958bc603 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -702,8 +702,6 @@ impl NonNull { #[unstable(feature = "non_null_convenience", issue = "117691")] #[rustc_const_unstable(feature = "non_null_convenience", issue = "117691")] #[must_use = "returns a new pointer rather than modifying its argument"] - // We could always go back to wrapping if unchecked becomes unacceptable - #[rustc_allow_const_fn_unstable(const_int_unchecked_arith)] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn sub(self, count: usize) -> Self @@ -1290,7 +1288,6 @@ impl NonNull { /// # Examples /// /// ``` - /// #![feature(pointer_is_aligned)] /// use std::ptr::NonNull; /// /// // On some platforms, the alignment of i32 is less than 4. @@ -1315,7 +1312,6 @@ impl NonNull { /// underlying allocation. /// /// ``` - /// #![feature(pointer_is_aligned)] /// #![feature(const_pointer_is_aligned)] /// #![feature(non_null_convenience)] /// #![feature(const_option)] @@ -1345,7 +1341,6 @@ impl NonNull { /// pointer is aligned, even if the compiletime pointer wasn't aligned. /// /// ``` - /// #![feature(pointer_is_aligned)] /// #![feature(const_pointer_is_aligned)] /// /// // On some platforms, the alignment of primitives is less than their size. @@ -1371,7 +1366,6 @@ impl NonNull { /// runtime and compiletime. /// /// ``` - /// #![feature(pointer_is_aligned)] /// #![feature(const_pointer_is_aligned)] /// #![feature(const_option)] /// #![feature(const_nonnull_new)] @@ -1396,7 +1390,7 @@ impl NonNull { /// ``` /// /// [tracking issue]: https://github.com/rust-lang/rust/issues/104203 - #[unstable(feature = "pointer_is_aligned", issue = "96284")] + #[stable(feature = "pointer_is_aligned", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")] #[must_use] #[inline] @@ -1419,7 +1413,7 @@ impl NonNull { /// # Examples /// /// ``` - /// #![feature(pointer_is_aligned)] + /// #![feature(pointer_is_aligned_to)] /// /// // On some platforms, the alignment of i32 is less than 4. /// #[repr(align(4))] @@ -1448,7 +1442,7 @@ impl NonNull { /// cannot be stricter aligned than the reference's underlying allocation. /// /// ``` - /// #![feature(pointer_is_aligned)] + /// #![feature(pointer_is_aligned_to)] /// #![feature(const_pointer_is_aligned)] /// /// // On some platforms, the alignment of i32 is less than 4. @@ -1473,7 +1467,7 @@ impl NonNull { /// pointer is aligned, even if the compiletime pointer wasn't aligned. /// /// ``` - /// #![feature(pointer_is_aligned)] + /// #![feature(pointer_is_aligned_to)] /// #![feature(const_pointer_is_aligned)] /// /// // On some platforms, the alignment of i32 is less than 4. @@ -1497,7 +1491,7 @@ impl NonNull { /// runtime and compiletime. /// /// ``` - /// #![feature(pointer_is_aligned)] + /// #![feature(pointer_is_aligned_to)] /// #![feature(const_pointer_is_aligned)] /// /// const _: () = { @@ -1511,7 +1505,7 @@ impl NonNull { /// ``` /// /// [tracking issue]: https://github.com/rust-lang/rust/issues/104203 - #[unstable(feature = "pointer_is_aligned", issue = "96284")] + #[unstable(feature = "pointer_is_aligned_to", issue = "96284")] #[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")] #[must_use] #[inline] @@ -1821,6 +1815,7 @@ impl PartialEq for NonNull { #[stable(feature = "nonnull", since = "1.25.0")] impl Ord for NonNull { #[inline] + #[allow(ambiguous_wide_pointer_comparisons)] fn cmp(&self, other: &Self) -> Ordering { self.as_ptr().cmp(&other.as_ptr()) } @@ -1829,6 +1824,7 @@ impl Ord for NonNull { #[stable(feature = "nonnull", since = "1.25.0")] impl PartialOrd for NonNull { #[inline] + #[allow(ambiguous_wide_pointer_comparisons)] fn partial_cmp(&self, other: &Self) -> Option { self.as_ptr().partial_cmp(&other.as_ptr()) } diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index 127a407dae5d4..8d7b6165510a8 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -195,6 +195,7 @@ pub unsafe trait SliceIndex: private_slice_index::Sealed { fn index_mut(self, slice: &mut T) -> &mut Self::Output; } +/// The methods `index` and `index_mut` panic if the index is out of bounds. #[stable(feature = "slice_get_slice_impls", since = "1.15.0")] #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] unsafe impl SliceIndex<[T]> for usize { @@ -328,6 +329,9 @@ unsafe impl SliceIndex<[T]> for ops::IndexRange { } } +/// The methods `index` and `index_mut` panic if: +/// - the start of the range is greater than the end of the range or +/// - the end of the range is out of bounds. #[stable(feature = "slice_get_slice_impls", since = "1.15.0")] #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] unsafe impl SliceIndex<[T]> for ops::Range { @@ -416,6 +420,7 @@ unsafe impl SliceIndex<[T]> for ops::Range { } } +/// The methods `index` and `index_mut` panic if the end of the range is out of bounds. #[stable(feature = "slice_get_slice_impls", since = "1.15.0")] #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] unsafe impl SliceIndex<[T]> for ops::RangeTo { @@ -454,6 +459,7 @@ unsafe impl SliceIndex<[T]> for ops::RangeTo { } } +/// The methods `index` and `index_mut` panic if the start of the range is out of bounds. #[stable(feature = "slice_get_slice_impls", since = "1.15.0")] #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] unsafe impl SliceIndex<[T]> for ops::RangeFrom { @@ -536,6 +542,10 @@ unsafe impl SliceIndex<[T]> for ops::RangeFull { } } +/// The methods `index` and `index_mut` panic if: +/// - the end of the range is `usize::MAX` or +/// - the start of the range is greater than the end of the range or +/// - the end of the range is out of bounds. #[stable(feature = "inclusive_range", since = "1.26.0")] #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] unsafe impl SliceIndex<[T]> for ops::RangeInclusive { @@ -580,6 +590,7 @@ unsafe impl SliceIndex<[T]> for ops::RangeInclusive { } } +/// The methods `index` and `index_mut` panic if the end of the range is out of bounds. #[stable(feature = "inclusive_range", since = "1.26.0")] #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] unsafe impl SliceIndex<[T]> for ops::RangeToInclusive { diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index a16005abf464d..6e1ba74f72b33 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -2724,8 +2724,10 @@ impl [T] { /// ``` /// let mut s = vec![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]; /// let num = 42; - /// let idx = s.partition_point(|&x| x < num); - /// // The above is equivalent to `let idx = s.binary_search(&num).unwrap_or_else(|x| x);` + /// let idx = s.partition_point(|&x| x <= num); + /// // If `num` is unique, `s.partition_point(|&x| x < num)` (with `<`) is equivalent to + /// // `s.binary_search(&num).unwrap_or_else(|x| x)`, but using `<=` will allow `insert` + /// // to shift less elements. /// s.insert(idx, num); /// assert_eq!(s, [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]); /// ``` @@ -4175,7 +4177,7 @@ impl [T] { /// ``` /// let mut s = vec![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]; /// let num = 42; - /// let idx = s.partition_point(|&x| x < num); + /// let idx = s.partition_point(|&x| x <= num); /// s.insert(idx, num); /// assert_eq!(s, [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]); /// ``` diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 77002ef87aa8c..0a749fcb8f904 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -418,14 +418,12 @@ impl AtomicBool { /// # Examples /// /// ``` - /// #![feature(pointer_is_aligned)] /// use std::sync::atomic::{self, AtomicBool}; - /// use std::mem::align_of; /// /// // Get a pointer to an allocated value /// let ptr: *mut bool = Box::into_raw(Box::new(false)); /// - /// assert!(ptr.is_aligned_to(align_of::())); + /// assert!(ptr.cast::().is_aligned()); /// /// { /// // Create an atomic view of the allocated value @@ -1216,14 +1214,12 @@ impl AtomicPtr { /// # Examples /// /// ``` - /// #![feature(pointer_is_aligned)] /// use std::sync::atomic::{self, AtomicPtr}; - /// use std::mem::align_of; /// /// // Get a pointer to an allocated value /// let ptr: *mut *mut u8 = Box::into_raw(Box::new(std::ptr::null_mut())); /// - /// assert!(ptr.is_aligned_to(align_of::>())); + /// assert!(ptr.cast::>().is_aligned()); /// /// { /// // Create an atomic view of the allocated value @@ -2199,14 +2195,12 @@ macro_rules! atomic_int { /// # Examples /// /// ``` - /// #![feature(pointer_is_aligned)] #[doc = concat!($extra_feature, "use std::sync::atomic::{self, ", stringify!($atomic_type), "};")] - /// use std::mem::align_of; /// /// // Get a pointer to an allocated value #[doc = concat!("let ptr: *mut ", stringify!($int_type), " = Box::into_raw(Box::new(0));")] /// - #[doc = concat!("assert!(ptr.is_aligned_to(align_of::<", stringify!($atomic_type), ">()));")] + #[doc = concat!("assert!(ptr.cast::<", stringify!($atomic_type), ">().is_aligned());")] /// /// { /// // Create an atomic view of the allocated value diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 1b43c46bda515..fe39f6c0b5691 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -2,6 +2,7 @@ use crate::mem::transmute; +use crate::any::Any; use crate::fmt; use crate::marker::PhantomData; use crate::ptr; @@ -220,6 +221,12 @@ impl RawWakerVTable { } } +#[derive(Debug)] +enum ExtData<'a> { + Some(&'a mut dyn Any), + None(()), +} + /// The context of an asynchronous task. /// /// Currently, `Context` only serves to provide access to a [`&Waker`](Waker) @@ -229,6 +236,7 @@ impl RawWakerVTable { pub struct Context<'a> { waker: &'a Waker, local_waker: &'a LocalWaker, + ext: ExtData<'a>, // Ensure we future-proof against variance changes by forcing // the lifetime to be invariant (argument-position lifetimes // are contravariant while return-position lifetimes are @@ -257,6 +265,7 @@ impl<'a> Context<'a> { pub const fn waker(&self) -> &'a Waker { &self.waker } + /// Returns a reference to the [`LocalWaker`] for the current task. #[inline] #[unstable(feature = "local_waker", issue = "118959")] @@ -264,6 +273,17 @@ impl<'a> Context<'a> { pub const fn local_waker(&self) -> &'a LocalWaker { &self.local_waker } + + /// Returns a reference to the extension data for the current task. + #[inline] + #[unstable(feature = "context_ext", issue = "123392")] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + pub const fn ext(&mut self) -> &mut dyn Any { + match &mut self.ext { + ExtData::Some(data) => *data, + ExtData::None(unit) => unit, + } + } } #[stable(feature = "futures_api", since = "1.36.0")] @@ -300,6 +320,7 @@ impl fmt::Debug for Context<'_> { pub struct ContextBuilder<'a> { waker: &'a Waker, local_waker: &'a LocalWaker, + ext: ExtData<'a>, // Ensure we future-proof against variance changes by forcing // the lifetime to be invariant (argument-position lifetimes // are contravariant while return-position lifetimes are @@ -318,7 +339,39 @@ impl<'a> ContextBuilder<'a> { pub const fn from_waker(waker: &'a Waker) -> Self { // SAFETY: LocalWaker is just Waker without thread safety let local_waker = unsafe { transmute(waker) }; - Self { waker: waker, local_waker, _marker: PhantomData, _marker2: PhantomData } + Self { + waker: waker, + local_waker, + ext: ExtData::None(()), + _marker: PhantomData, + _marker2: PhantomData, + } + } + + /// Create a ContextBuilder from an existing Context. + #[inline] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + #[unstable(feature = "context_ext", issue = "123392")] + pub const fn from(cx: &'a mut Context<'_>) -> Self { + let ext = match &mut cx.ext { + ExtData::Some(ext) => ExtData::Some(*ext), + ExtData::None(()) => ExtData::None(()), + }; + Self { + waker: cx.waker, + local_waker: cx.local_waker, + ext, + _marker: PhantomData, + _marker2: PhantomData, + } + } + + /// This method is used to set the value for the waker on `Context`. + #[inline] + #[unstable(feature = "context_ext", issue = "123392")] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + pub const fn waker(self, waker: &'a Waker) -> Self { + Self { waker, ..self } } /// This method is used to set the value for the local waker on `Context`. @@ -329,13 +382,21 @@ impl<'a> ContextBuilder<'a> { Self { local_waker, ..self } } + /// This method is used to set the value for the extension data on `Context`. + #[inline] + #[unstable(feature = "context_ext", issue = "123392")] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + pub const fn ext(self, data: &'a mut dyn Any) -> Self { + Self { ext: ExtData::Some(data), ..self } + } + /// Builds the `Context`. #[inline] #[unstable(feature = "local_waker", issue = "118959")] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] pub const fn build(self) -> Context<'a> { - let ContextBuilder { waker, local_waker, _marker, _marker2 } = self; - Context { waker, local_waker, _marker, _marker2 } + let ContextBuilder { waker, local_waker, ext, _marker, _marker2 } = self; + Context { waker, local_waker, ext, _marker, _marker2 } } } diff --git a/library/core/tests/intrinsics.rs b/library/core/tests/intrinsics.rs index 740565d0df6b4..eb1e1a0b9b1dd 100644 --- a/library/core/tests/intrinsics.rs +++ b/library/core/tests/intrinsics.rs @@ -99,3 +99,30 @@ fn test_const_deallocate_at_runtime() { const_deallocate(core::ptr::null_mut(), 1, 1); // nop } } + +#[cfg(not(bootstrap))] +#[test] +fn test_three_way_compare_in_const_contexts() { + use core::cmp::Ordering::{self, *}; + use core::intrinsics::three_way_compare; + + const UNSIGNED_LESS: Ordering = three_way_compare(123_u16, 456); + const UNSIGNED_EQUAL: Ordering = three_way_compare(456_u16, 456); + const UNSIGNED_GREATER: Ordering = three_way_compare(789_u16, 456); + const CHAR_LESS: Ordering = three_way_compare('A', 'B'); + const CHAR_EQUAL: Ordering = three_way_compare('B', 'B'); + const CHAR_GREATER: Ordering = three_way_compare('C', 'B'); + const SIGNED_LESS: Ordering = three_way_compare(123_i64, 456); + const SIGNED_EQUAL: Ordering = three_way_compare(456_i64, 456); + const SIGNED_GREATER: Ordering = three_way_compare(789_i64, 456); + + assert_eq!(UNSIGNED_LESS, Less); + assert_eq!(UNSIGNED_EQUAL, Equal); + assert_eq!(UNSIGNED_GREATER, Greater); + assert_eq!(CHAR_LESS, Less); + assert_eq!(CHAR_EQUAL, Equal); + assert_eq!(CHAR_GREATER, Greater); + assert_eq!(SIGNED_LESS, Less); + assert_eq!(SIGNED_EQUAL, Equal); + assert_eq!(SIGNED_GREATER, Greater); +} diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 421062f5873cd..8c5e5ecf5fe9f 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -16,11 +16,13 @@ #![feature(const_hash)] #![feature(const_heap)] #![feature(const_intrinsic_copy)] +#![feature(const_int_from_str)] #![feature(const_maybe_uninit_as_mut_ptr)] #![feature(const_nonnull_new)] #![feature(const_pointer_is_aligned)] #![feature(const_ptr_as_ref)] #![feature(const_ptr_write)] +#![cfg_attr(not(bootstrap), feature(const_three_way_compare))] #![feature(const_trait_impl)] #![feature(const_likely)] #![feature(const_location_fields)] @@ -36,6 +38,7 @@ #![feature(duration_constructors)] #![feature(exact_size_is_empty)] #![feature(extern_types)] +#![feature(freeze)] #![feature(flt2dec)] #![feature(fmt_internals)] #![feature(float_minimum_maximum)] @@ -95,7 +98,7 @@ #![feature(const_waker)] #![feature(never_type)] #![feature(unwrap_infallible)] -#![feature(pointer_is_aligned)] +#![feature(pointer_is_aligned_to)] #![feature(portable_simd)] #![feature(ptr_metadata)] #![feature(lazy_cell)] diff --git a/library/core/tests/num/mod.rs b/library/core/tests/num/mod.rs index 863da9b18a289..0fed854318d54 100644 --- a/library/core/tests/num/mod.rs +++ b/library/core/tests/num/mod.rs @@ -214,6 +214,16 @@ fn test_infallible_try_from_int_error() { assert!(func(0).is_ok()); } +const _TEST_CONST_PARSE: () = { + let Ok(-0x8000) = i16::from_str_radix("-8000", 16) else { panic!() }; + let Ok(12345) = u64::from_str_radix("12345", 10) else { panic!() }; + if let Err(e) = i8::from_str_radix("+", 10) { + let IntErrorKind::InvalidDigit = e.kind() else { panic!() }; + } else { + panic!() + } +}; + macro_rules! test_impl_from { ($fn_name:ident, bool, $target: ty) => { #[test] diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs index 5c518e2d59340..2c82eda9a58c1 100644 --- a/library/core/tests/ptr.rs +++ b/library/core/tests/ptr.rs @@ -1,4 +1,5 @@ use core::cell::RefCell; +use core::marker::Freeze; use core::mem::{self, MaybeUninit}; use core::num::NonZero; use core::ptr; @@ -841,11 +842,19 @@ fn ptr_metadata_bounds() { fn static_assert_expected_bounds_for_metadata() where // Keep this in sync with the associated type in `library/core/src/ptr/metadata.rs` - Meta: Copy + Send + Sync + Ord + std::hash::Hash + Unpin, + Meta: Debug + Copy + Send + Sync + Ord + std::hash::Hash + Unpin + Freeze, { } } +#[test] +fn pointee_metadata_debug() { + assert_eq!("()", format!("{:?}", metadata::(&17))); + assert_eq!("2", format!("{:?}", metadata::<[u32]>(&[19, 23]))); + let for_dyn = format!("{:?}", metadata::(&29)); + assert!(for_dyn.starts_with("DynMetadata(0x"), "{:?}", for_dyn); +} + #[test] fn dyn_metadata() { #[derive(Debug)] diff --git a/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs b/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs index e217d1c8c87ca..0f1719206c9ce 100644 --- a/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs +++ b/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs @@ -50,14 +50,14 @@ pub trait SimdConstPtr: Copy + Sealed { /// Equivalent to calling [`pointer::with_addr`] on each element. fn with_addr(self, addr: Self::Usize) -> Self; - /// Gets the "address" portion of the pointer, and "exposes" the provenance part for future use - /// in [`Self::from_exposed_addr`]. - fn expose_addr(self) -> Self::Usize; + /// Exposes the "provenance" part of the pointer for future use in + /// [`Self::with_exposed_provenance`] and returns the "address" portion. + fn expose_provenance(self) -> Self::Usize; /// Convert an address back to a pointer, picking up a previously "exposed" provenance. /// - /// Equivalent to calling [`core::ptr::from_exposed_addr`] on each element. - fn from_exposed_addr(addr: Self::Usize) -> Self; + /// Equivalent to calling [`core::ptr::with_exposed_provenance`] on each element. + fn with_exposed_provenance(addr: Self::Usize) -> Self; /// Calculates the offset from a pointer using wrapping arithmetic. /// @@ -131,15 +131,15 @@ where } #[inline] - fn expose_addr(self) -> Self::Usize { + fn expose_provenance(self) -> Self::Usize { // Safety: `self` is a pointer vector - unsafe { core::intrinsics::simd::simd_expose_addr(self) } + unsafe { core::intrinsics::simd::simd_expose_provenance(self) } } #[inline] - fn from_exposed_addr(addr: Self::Usize) -> Self { + fn with_exposed_provenance(addr: Self::Usize) -> Self { // Safety: `self` is a pointer vector - unsafe { core::intrinsics::simd::simd_from_exposed_addr(addr) } + unsafe { core::intrinsics::simd::simd_with_exposed_provenance(addr) } } #[inline] diff --git a/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs b/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs index 5cb27af4fdeba..7ba996d149c0c 100644 --- a/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs +++ b/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs @@ -47,14 +47,14 @@ pub trait SimdMutPtr: Copy + Sealed { /// Equivalent to calling [`pointer::with_addr`] on each element. fn with_addr(self, addr: Self::Usize) -> Self; - /// Gets the "address" portion of the pointer, and "exposes" the provenance part for future use - /// in [`Self::from_exposed_addr`]. - fn expose_addr(self) -> Self::Usize; + /// Exposes the "provenance" part of the pointer for future use in + /// [`Self::with_exposed_provenance`] and returns the "address" portion. + fn expose_provenance(self) -> Self::Usize; /// Convert an address back to a pointer, picking up a previously "exposed" provenance. /// - /// Equivalent to calling [`core::ptr::from_exposed_addr_mut`] on each element. - fn from_exposed_addr(addr: Self::Usize) -> Self; + /// Equivalent to calling [`core::ptr::with_exposed_provenance_mut`] on each element. + fn with_exposed_provenance(addr: Self::Usize) -> Self; /// Calculates the offset from a pointer using wrapping arithmetic. /// @@ -128,15 +128,15 @@ where } #[inline] - fn expose_addr(self) -> Self::Usize { + fn expose_provenance(self) -> Self::Usize { // Safety: `self` is a pointer vector - unsafe { core::intrinsics::simd::simd_expose_addr(self) } + unsafe { core::intrinsics::simd::simd_expose_provenance(self) } } #[inline] - fn from_exposed_addr(addr: Self::Usize) -> Self { + fn with_exposed_provenance(addr: Self::Usize) -> Self { // Safety: `self` is a pointer vector - unsafe { core::intrinsics::simd::simd_from_exposed_addr(addr) } + unsafe { core::intrinsics::simd::simd_with_exposed_provenance(addr) } } #[inline] diff --git a/library/portable-simd/crates/core_simd/tests/pointers.rs b/library/portable-simd/crates/core_simd/tests/pointers.rs index b9f32d16e01d1..90bfc5d5fd6a5 100644 --- a/library/portable-simd/crates/core_simd/tests/pointers.rs +++ b/library/portable-simd/crates/core_simd/tests/pointers.rs @@ -32,10 +32,10 @@ macro_rules! common_tests { ); } - fn expose_addr() { + fn expose_provenance() { test_helpers::test_unary_elementwise( - &Simd::<*$constness u32, LANES>::expose_addr, - &<*$constness u32>::expose_addr, + &Simd::<*$constness u32, LANES>::expose_provenance, + &<*$constness u32>::expose_provenance, &|_| true, ); } @@ -80,10 +80,10 @@ mod const_ptr { ); } - fn from_exposed_addr() { + fn with_exposed_provenance() { test_helpers::test_unary_elementwise( - &Simd::<*const u32, LANES>::from_exposed_addr, - &core::ptr::from_exposed_addr::, + &Simd::<*const u32, LANES>::with_exposed_provenance, + &core::ptr::with_exposed_provenance::, &|_| true, ); } @@ -103,10 +103,10 @@ mod mut_ptr { ); } - fn from_exposed_addr() { + fn with_exposed_provenance() { test_helpers::test_unary_elementwise( - &Simd::<*mut u32, LANES>::from_exposed_addr, - &core::ptr::from_exposed_addr_mut::, + &Simd::<*mut u32, LANES>::with_exposed_provenance, + &core::ptr::with_exposed_provenance_mut::, &|_| true, ); } diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index e04bf69ef5117..01c449563ee92 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -1360,7 +1360,7 @@ impl Literal { } /// Byte character literal. - #[unstable(feature = "proc_macro_byte_character", issue = "115268")] + #[stable(feature = "proc_macro_byte_character", since = "CURRENT_RUSTC_VERSION")] pub fn byte_character(byte: u8) -> Literal { let string = [byte].escape_ascii().to_string(); Literal::new(bridge::LitKind::Byte, &string, None) @@ -1374,7 +1374,7 @@ impl Literal { } /// C string literal. - #[unstable(feature = "proc_macro_c_str_literals", issue = "119750")] + #[stable(feature = "proc_macro_c_str_literals", since = "CURRENT_RUSTC_VERSION")] pub fn c_string(string: &CStr) -> Literal { let string = string.to_bytes().escape_ascii().to_string(); Literal::new(bridge::LitKind::CStr, &string, None) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 9017ba7971463..b1102b440e02a 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -37,7 +37,7 @@ use crate::time::SystemTime; /// /// # Examples /// -/// Creates a new file and write bytes to it (you can also use [`write()`]): +/// Creates a new file and write bytes to it (you can also use [`write`]): /// /// ```no_run /// use std::fs::File; @@ -2018,7 +2018,7 @@ pub fn rename, Q: AsRef>(from: P, to: Q) -> io::Result<()> /// the length of the `to` file as reported by `metadata`. /// /// If you want to copy the contents of one file to another and you’re -/// working with [`File`]s, see the [`io::copy()`] function. +/// working with [`File`]s, see the [`io::copy`](io::copy()) function. /// /// # Platform-specific behavior /// diff --git a/library/std/src/lib.miri.rs b/library/std/src/lib.miri.rs new file mode 100644 index 0000000000000..1f9bfb5b1b5c0 --- /dev/null +++ b/library/std/src/lib.miri.rs @@ -0,0 +1,4 @@ +//! Grep bootstrap for `MIRI_REPLACE_LIBRS_IF_NOT_TEST` to learn what this is about. +#![no_std] +extern crate std as realstd; +pub use realstd::*; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index dc5a8704498d5..31a8711e0eb97 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -221,6 +221,7 @@ // #![cfg_attr(not(feature = "restricted-std"), stable(feature = "rust1", since = "1.0.0"))] #![cfg_attr(feature = "restricted-std", unstable(feature = "restricted_std", issue = "none"))] +#![cfg_attr(not(bootstrap), rustc_preserve_ub_checks)] #![doc( html_playground_url = "https://play.rust-lang.org/", issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/", @@ -340,7 +341,7 @@ #![feature(panic_can_unwind)] #![feature(panic_info_message)] #![feature(panic_internals)] -#![feature(pointer_is_aligned)] +#![feature(pointer_is_aligned_to)] #![feature(portable_simd)] #![feature(prelude_2024)] #![feature(ptr_as_uninit)] diff --git a/library/std/src/os/xous/ffi.rs b/library/std/src/os/xous/ffi.rs index 7fe84db515c34..e9a9f53372026 100644 --- a/library/std/src/os/xous/ffi.rs +++ b/library/std/src/os/xous/ffi.rs @@ -389,7 +389,7 @@ pub(crate) unsafe fn map_memory( let result = a0; if result == SyscallResult::MemoryRange as usize { - let start = core::ptr::from_exposed_addr_mut::(a1); + let start = core::ptr::with_exposed_provenance_mut::(a1); let len = a2 / core::mem::size_of::(); let end = unsafe { start.add(len) }; Ok(unsafe { core::slice::from_raw_parts_mut(start, len) }) diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index 3d576af681e03..e63b46ab70548 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -126,6 +126,9 @@ where /// Also note that unwinding into Rust code with a foreign exception (e.g. /// an exception thrown from C++ code) is undefined behavior. /// +/// Finally, be **careful in how you drop the result of this function**. +/// If it is `Err`, it contains the panic payload, and dropping that may in turn panic! +/// /// # Examples /// /// ``` diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 31dbe86b66c70..f46e1e171d251 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -21,7 +21,6 @@ use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sync::{PoisonError, RwLock}; use crate::sys::stdio::panic_output; use crate::sys_common::backtrace; -use crate::sys_common::thread_info; use crate::thread; #[cfg(not(test))] @@ -256,7 +255,7 @@ fn default_hook(info: &PanicInfo<'_>) { None => "Box", }, }; - let thread = thread_info::current_thread(); + let thread = thread::try_current(); let name = thread.as_ref().and_then(|t| t.name()).unwrap_or(""); let write = |err: &mut dyn crate::io::Write| { diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index 335944845ae0b..ff6e433ebce38 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -24,8 +24,7 @@ pub use core::panicking::{panic_display, panic_fmt}; use crate::sync::Once; use crate::sys; -use crate::sys_common::thread_info; -use crate::thread::Thread; +use crate::thread::{self, Thread}; // Prints to the "panic output", depending on the platform this may be: // - the standard error output @@ -96,13 +95,9 @@ unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { unsafe { sys::init(argc, argv, sigpipe); - let main_guard = sys::thread::guard::init(); - // Next, set up the current Thread with the guard information we just - // created. Note that this isn't necessary in general for new threads, - // but we just do this to name the main thread and to give it correct - // info about the stack bounds. + // Set up the current thread to give it the right name. let thread = Thread::new(Some(rtunwrap!(Ok, CString::new("main")))); - thread_info::set(main_guard, thread); + thread::set_current(thread); } } diff --git a/library/std/src/sys/pal/hermit/thread.rs b/library/std/src/sys/pal/hermit/thread.rs index cf45b9c23962c..40f88e33d4ad3 100644 --- a/library/std/src/sys/pal/hermit/thread.rs +++ b/library/std/src/sys/pal/hermit/thread.rs @@ -29,7 +29,7 @@ impl Thread { let p = Box::into_raw(Box::new(p)); let tid = abi::spawn2( thread_start, - p.expose_addr(), + p.expose_provenance(), abi::Priority::into(abi::NORMAL_PRIO), stack, core_id, @@ -47,7 +47,7 @@ impl Thread { extern "C" fn thread_start(main: usize) { unsafe { // Finally, let's run some code. - Box::from_raw(ptr::from_exposed_addr::>(main).cast_mut())(); + Box::from_raw(ptr::with_exposed_provenance::>(main).cast_mut())(); // run all destructors run_dtors(); @@ -104,13 +104,3 @@ impl Thread { pub fn available_parallelism() -> io::Result> { unsafe { Ok(NonZero::new_unchecked(abi::get_processor_count())) } } - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/library/std/src/sys/pal/itron/thread.rs b/library/std/src/sys/pal/itron/thread.rs index 814a102dd09ae..047513a67927a 100644 --- a/library/std/src/sys/pal/itron/thread.rs +++ b/library/std/src/sys/pal/itron/thread.rs @@ -98,7 +98,7 @@ impl Thread { }); unsafe extern "C" fn trampoline(exinf: isize) { - let p_inner: *mut ThreadInner = crate::ptr::from_exposed_addr_mut(exinf as usize); + let p_inner: *mut ThreadInner = crate::ptr::with_exposed_provenance_mut(exinf as usize); // Safety: `ThreadInner` is alive at this point let inner = unsafe { &*p_inner }; @@ -181,7 +181,7 @@ impl Thread { abi::acre_tsk(&abi::T_CTSK { // Activate this task immediately tskatr: abi::TA_ACT, - exinf: p_inner.as_ptr().expose_addr() as abi::EXINF, + exinf: p_inner.as_ptr().expose_provenance() as abi::EXINF, // The entry point task: Some(trampoline), // Inherit the calling task's base priority @@ -312,16 +312,6 @@ impl Drop for Thread { } } -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} - /// Terminate and delete the specified task. /// /// This function will abort if `deleted_task` refers to the calling task. diff --git a/library/std/src/sys/pal/mod.rs b/library/std/src/sys/pal/mod.rs index 7c87deed3715a..8c75ac652998b 100644 --- a/library/std/src/sys/pal/mod.rs +++ b/library/std/src/sys/pal/mod.rs @@ -37,12 +37,12 @@ cfg_if::cfg_if! { } else if #[cfg(target_os = "hermit")] { mod hermit; pub use self::hermit::*; - } else if #[cfg(target_os = "wasi")] { - mod wasi; - pub use self::wasi::*; } else if #[cfg(all(target_os = "wasi", target_env = "p2"))] { mod wasip2; pub use self::wasip2::*; + } else if #[cfg(target_os = "wasi")] { + mod wasi; + pub use self::wasi::*; } else if #[cfg(target_family = "wasm")] { mod wasm; pub use self::wasm::*; diff --git a/library/std/src/sys/pal/sgx/thread.rs b/library/std/src/sys/pal/sgx/thread.rs index 77f68bf73348f..ef07f6e6a263c 100644 --- a/library/std/src/sys/pal/sgx/thread.rs +++ b/library/std/src/sys/pal/sgx/thread.rs @@ -149,13 +149,3 @@ impl Thread { pub fn available_parallelism() -> io::Result> { unsupported() } - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/library/std/src/sys/pal/teeos/thread.rs b/library/std/src/sys/pal/teeos/thread.rs index b76bcf9bbb0af..fb4b74ba3c36a 100644 --- a/library/std/src/sys/pal/teeos/thread.rs +++ b/library/std/src/sys/pal/teeos/thread.rs @@ -151,18 +151,6 @@ pub fn available_parallelism() -> io::Result> { )) } -// stub -pub mod guard { - use crate::ops::Range; - pub type Guard = Range; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} - fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { libc::PTHREAD_STACK_MIN.try_into().expect("Infallible") } diff --git a/library/std/src/sys/pal/uefi/thread.rs b/library/std/src/sys/pal/uefi/thread.rs index b3a4f9c53e36c..ca7b1efc26999 100644 --- a/library/std/src/sys/pal/uefi/thread.rs +++ b/library/std/src/sys/pal/uefi/thread.rs @@ -52,13 +52,3 @@ pub fn available_parallelism() -> io::Result> { // UEFI is single threaded Ok(NonZero::new(1).unwrap()) } - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/library/std/src/sys/pal/unix/rand.rs b/library/std/src/sys/pal/unix/rand.rs index c9ed6825f6c21..d52c3254e5ebf 100644 --- a/library/std/src/sys/pal/unix/rand.rs +++ b/library/std/src/sys/pal/unix/rand.rs @@ -62,17 +62,23 @@ mod imp { unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), libc::GRND_NONBLOCK) } } - #[cfg(any( - target_os = "espidf", - target_os = "horizon", - target_os = "freebsd", - target_os = "dragonfly", - netbsd10 - ))] + #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "freebsd", netbsd10))] fn getrandom(buf: &mut [u8]) -> libc::ssize_t { unsafe { libc::getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) } } + #[cfg(target_os = "dragonfly")] + fn getrandom(buf: &mut [u8]) -> libc::ssize_t { + extern "C" { + fn getrandom( + buf: *mut libc::c_void, + buflen: libc::size_t, + flags: libc::c_uint, + ) -> libc::ssize_t; + } + unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) } + } + #[cfg(not(any( target_os = "linux", target_os = "android", diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 78a599077c758..26c49257ad00d 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -11,7 +11,7 @@ pub struct Handler { impl Handler { pub unsafe fn new() -> Handler { - make_handler() + make_handler(false) } fn null() -> Handler { @@ -29,34 +29,41 @@ impl Drop for Handler { #[cfg(any( target_os = "linux", - target_os = "macos", - target_os = "dragonfly", target_os = "freebsd", target_os = "hurd", - target_os = "solaris", - target_os = "illumos", + target_os = "macos", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", + target_os = "solaris" ))] mod imp { use super::Handler; + use crate::cell::Cell; use crate::io; use crate::mem; + use crate::ops::Range; use crate::ptr; + use crate::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering}; + use crate::sys::pal::unix::os; use crate::thread; - use libc::MAP_FAILED; #[cfg(not(all(target_os = "linux", target_env = "gnu")))] - use libc::{mmap as mmap64, munmap}; + use libc::{mmap as mmap64, mprotect, munmap}; #[cfg(all(target_os = "linux", target_env = "gnu"))] - use libc::{mmap64, munmap}; - use libc::{sigaction, sighandler_t, SA_ONSTACK, SA_SIGINFO, SIGBUS, SIG_DFL}; + use libc::{mmap64, mprotect, munmap}; + use libc::{sigaction, sighandler_t, SA_ONSTACK, SA_SIGINFO, SIGBUS, SIGSEGV, SIG_DFL}; use libc::{sigaltstack, SS_DISABLE}; - use libc::{MAP_ANON, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE, SIGSEGV}; + use libc::{MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE}; - use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; - use crate::sys::pal::unix::os::page_size; - use crate::sys_common::thread_info; + // We use a TLS variable to store the address of the guard page. While TLS + // variables are not guaranteed to be signal-safe, this works out in practice + // since we make sure to write to the variable before the signal stack is + // installed, thereby ensuring that the variable is always allocated when + // the signal handler is called. + thread_local! { + // FIXME: use `Range` once that implements `Copy`. + static GUARD: Cell<(usize, usize)> = const { Cell::new((0, 0)) }; + } // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages // (unmapped pages) at the end of every thread's stack, so if a thread ends @@ -84,12 +91,12 @@ mod imp { info: *mut libc::siginfo_t, _data: *mut libc::c_void, ) { - let guard = thread_info::stack_guard().unwrap_or(0..0); + let (start, end) = GUARD.get(); let addr = (*info).si_addr() as usize; // If the faulting address is within the guard page, then we print a // message saying so and abort. - if guard.start <= addr && addr < guard.end { + if start <= addr && addr < end { rtprintpanic!( "\nthread '{}' has overflowed its stack\n", thread::current().name().unwrap_or("") @@ -105,10 +112,17 @@ mod imp { } } + static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0); static MAIN_ALTSTACK: AtomicPtr = AtomicPtr::new(ptr::null_mut()); static NEED_ALTSTACK: AtomicBool = AtomicBool::new(false); pub unsafe fn init() { + PAGE_SIZE.store(os::page_size(), Ordering::Relaxed); + + // Always write to GUARD to ensure the TLS variable is allocated. + let guard = install_main_guard().unwrap_or(0..0); + GUARD.set((guard.start, guard.end)); + let mut action: sigaction = mem::zeroed(); for &signal in &[SIGSEGV, SIGBUS] { sigaction(signal, ptr::null_mut(), &mut action); @@ -121,7 +135,7 @@ mod imp { } } - let handler = make_handler(); + let handler = make_handler(true); MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed); mem::forget(handler); } @@ -150,7 +164,7 @@ mod imp { let flags = MAP_PRIVATE | MAP_ANON; let sigstack_size = sigstack_size(); - let page_size = page_size(); + let page_size = PAGE_SIZE.load(Ordering::Relaxed); let stackp = mmap64( ptr::null_mut(), @@ -172,10 +186,17 @@ mod imp { libc::stack_t { ss_sp: stackp, ss_flags: 0, ss_size: sigstack_size } } - pub unsafe fn make_handler() -> Handler { + pub unsafe fn make_handler(main_thread: bool) -> Handler { if !NEED_ALTSTACK.load(Ordering::Relaxed) { return Handler::null(); } + + if !main_thread { + // Always write to GUARD to ensure the TLS variable is allocated. + let guard = current_guard().unwrap_or(0..0); + GUARD.set((guard.start, guard.end)); + } + let mut stack = mem::zeroed(); sigaltstack(ptr::null(), &mut stack); // Configure alternate signal stack, if one is not already set. @@ -191,7 +212,7 @@ mod imp { pub unsafe fn drop_handler(data: *mut libc::c_void) { if !data.is_null() { let sigstack_size = sigstack_size(); - let page_size = page_size(); + let page_size = PAGE_SIZE.load(Ordering::Relaxed); let stack = libc::stack_t { ss_sp: ptr::null_mut(), ss_flags: SS_DISABLE, @@ -225,25 +246,266 @@ mod imp { fn sigstack_size() -> usize { libc::SIGSTKSZ } + + #[cfg(target_os = "solaris")] + unsafe fn get_stack_start() -> Option<*mut libc::c_void> { + let mut current_stack: libc::stack_t = crate::mem::zeroed(); + assert_eq!(libc::stack_getbounds(&mut current_stack), 0); + Some(current_stack.ss_sp) + } + + #[cfg(target_os = "macos")] + unsafe fn get_stack_start() -> Option<*mut libc::c_void> { + let th = libc::pthread_self(); + let stackptr = libc::pthread_get_stackaddr_np(th); + Some(stackptr.map_addr(|addr| addr - libc::pthread_get_stacksize_np(th))) + } + + #[cfg(target_os = "openbsd")] + unsafe fn get_stack_start() -> Option<*mut libc::c_void> { + let mut current_stack: libc::stack_t = crate::mem::zeroed(); + assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(), &mut current_stack), 0); + + let stack_ptr = current_stack.ss_sp; + let stackaddr = if libc::pthread_main_np() == 1 { + // main thread + stack_ptr.addr() - current_stack.ss_size + PAGE_SIZE.load(Ordering::Relaxed) + } else { + // new thread + stack_ptr.addr() - current_stack.ss_size + }; + Some(stack_ptr.with_addr(stackaddr)) + } + + #[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", + target_os = "hurd", + target_os = "linux", + target_os = "l4re" + ))] + unsafe fn get_stack_start() -> Option<*mut libc::c_void> { + let mut ret = None; + let mut attr: libc::pthread_attr_t = crate::mem::zeroed(); + #[cfg(target_os = "freebsd")] + assert_eq!(libc::pthread_attr_init(&mut attr), 0); + #[cfg(target_os = "freebsd")] + let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr); + #[cfg(not(target_os = "freebsd"))] + let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr); + if e == 0 { + let mut stackaddr = crate::ptr::null_mut(); + let mut stacksize = 0; + assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize), 0); + ret = Some(stackaddr); + } + if e == 0 || cfg!(target_os = "freebsd") { + assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); + } + ret + } + + unsafe fn get_stack_start_aligned() -> Option<*mut libc::c_void> { + let page_size = PAGE_SIZE.load(Ordering::Relaxed); + let stackptr = get_stack_start()?; + let stackaddr = stackptr.addr(); + + // Ensure stackaddr is page aligned! A parent process might + // have reset RLIMIT_STACK to be non-page aligned. The + // pthread_attr_getstack() reports the usable stack area + // stackaddr < stackaddr + stacksize, so if stackaddr is not + // page-aligned, calculate the fix such that stackaddr < + // new_page_aligned_stackaddr < stackaddr + stacksize + let remainder = stackaddr % page_size; + Some(if remainder == 0 { + stackptr + } else { + stackptr.with_addr(stackaddr + page_size - remainder) + }) + } + + unsafe fn install_main_guard() -> Option> { + let page_size = PAGE_SIZE.load(Ordering::Relaxed); + if cfg!(all(target_os = "linux", not(target_env = "musl"))) { + // Linux doesn't allocate the whole stack right away, and + // the kernel has its own stack-guard mechanism to fault + // when growing too close to an existing mapping. If we map + // our own guard, then the kernel starts enforcing a rather + // large gap above that, rendering much of the possible + // stack space useless. See #43052. + // + // Instead, we'll just note where we expect rlimit to start + // faulting, so our handler can report "stack overflow", and + // trust that the kernel's own stack guard will work. + let stackptr = get_stack_start_aligned()?; + let stackaddr = stackptr.addr(); + Some(stackaddr - page_size..stackaddr) + } else if cfg!(all(target_os = "linux", target_env = "musl")) { + // For the main thread, the musl's pthread_attr_getstack + // returns the current stack size, rather than maximum size + // it can eventually grow to. It cannot be used to determine + // the position of kernel's stack guard. + None + } else if cfg!(target_os = "freebsd") { + // FreeBSD's stack autogrows, and optionally includes a guard page + // at the bottom. If we try to remap the bottom of the stack + // ourselves, FreeBSD's guard page moves upwards. So we'll just use + // the builtin guard page. + let stackptr = get_stack_start_aligned()?; + let guardaddr = stackptr.addr(); + // Technically the number of guard pages is tunable and controlled + // by the security.bsd.stack_guard_page sysctl. + // By default it is 1, checking once is enough since it is + // a boot time config value. + static PAGES: crate::sync::OnceLock = crate::sync::OnceLock::new(); + + let pages = PAGES.get_or_init(|| { + use crate::sys::weak::dlsym; + dlsym!(fn sysctlbyname(*const libc::c_char, *mut libc::c_void, *mut libc::size_t, *const libc::c_void, libc::size_t) -> libc::c_int); + let mut guard: usize = 0; + let mut size = crate::mem::size_of_val(&guard); + let oid = crate::ffi::CStr::from_bytes_with_nul( + b"security.bsd.stack_guard_page\0", + ) + .unwrap(); + match sysctlbyname.get() { + Some(fcn) => { + if fcn(oid.as_ptr(), core::ptr::addr_of_mut!(guard) as *mut _, core::ptr::addr_of_mut!(size) as *mut _, crate::ptr::null_mut(), 0) == 0 { + guard + } else { + 1 + } + }, + _ => 1, + } + }); + Some(guardaddr..guardaddr + pages * page_size) + } else if cfg!(any(target_os = "openbsd", target_os = "netbsd")) { + // OpenBSD stack already includes a guard page, and stack is + // immutable. + // NetBSD stack includes the guard page. + // + // We'll just note where we expect rlimit to start + // faulting, so our handler can report "stack overflow", and + // trust that the kernel's own stack guard will work. + let stackptr = get_stack_start_aligned()?; + let stackaddr = stackptr.addr(); + Some(stackaddr - page_size..stackaddr) + } else { + // Reallocate the last page of the stack. + // This ensures SIGBUS will be raised on + // stack overflow. + // Systems which enforce strict PAX MPROTECT do not allow + // to mprotect() a mapping with less restrictive permissions + // than the initial mmap() used, so we mmap() here with + // read/write permissions and only then mprotect() it to + // no permissions at all. See issue #50313. + let stackptr = get_stack_start_aligned()?; + let result = mmap64( + stackptr, + page_size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED, + -1, + 0, + ); + if result != stackptr || result == MAP_FAILED { + panic!("failed to allocate a guard page: {}", io::Error::last_os_error()); + } + + let result = mprotect(stackptr, page_size, PROT_NONE); + if result != 0 { + panic!("failed to protect the guard page: {}", io::Error::last_os_error()); + } + + let guardaddr = stackptr.addr(); + + Some(guardaddr..guardaddr + page_size) + } + } + + #[cfg(any(target_os = "macos", target_os = "openbsd", target_os = "solaris"))] + unsafe fn current_guard() -> Option> { + let stackptr = get_stack_start()?; + let stackaddr = stackptr.addr(); + Some(stackaddr - PAGE_SIZE.load(Ordering::Relaxed)..stackaddr) + } + + #[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "hurd", + target_os = "linux", + target_os = "netbsd", + target_os = "l4re" + ))] + unsafe fn current_guard() -> Option> { + let mut ret = None; + let mut attr: libc::pthread_attr_t = crate::mem::zeroed(); + #[cfg(target_os = "freebsd")] + assert_eq!(libc::pthread_attr_init(&mut attr), 0); + #[cfg(target_os = "freebsd")] + let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr); + #[cfg(not(target_os = "freebsd"))] + let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr); + if e == 0 { + let mut guardsize = 0; + assert_eq!(libc::pthread_attr_getguardsize(&attr, &mut guardsize), 0); + if guardsize == 0 { + if cfg!(all(target_os = "linux", target_env = "musl")) { + // musl versions before 1.1.19 always reported guard + // size obtained from pthread_attr_get_np as zero. + // Use page size as a fallback. + guardsize = PAGE_SIZE.load(Ordering::Relaxed); + } else { + panic!("there is no guard page"); + } + } + let mut stackptr = crate::ptr::null_mut::(); + let mut size = 0; + assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackptr, &mut size), 0); + + let stackaddr = stackptr.addr(); + ret = if cfg!(any(target_os = "freebsd", target_os = "netbsd", target_os = "hurd")) { + Some(stackaddr - guardsize..stackaddr) + } else if cfg!(all(target_os = "linux", target_env = "musl")) { + Some(stackaddr - guardsize..stackaddr) + } else if cfg!(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc"))) + { + // glibc used to include the guard area within the stack, as noted in the BUGS + // section of `man pthread_attr_getguardsize`. This has been corrected starting + // with glibc 2.27, and in some distro backports, so the guard is now placed at the + // end (below) the stack. There's no easy way for us to know which we have at + // runtime, so we'll just match any fault in the range right above or below the + // stack base to call that fault a stack overflow. + Some(stackaddr - guardsize..stackaddr + guardsize) + } else { + Some(stackaddr..stackaddr + guardsize) + }; + } + if e == 0 || cfg!(target_os = "freebsd") { + assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); + } + ret + } } #[cfg(not(any( target_os = "linux", - target_os = "macos", - target_os = "dragonfly", target_os = "freebsd", target_os = "hurd", - target_os = "solaris", - target_os = "illumos", + target_os = "macos", target_os = "netbsd", target_os = "openbsd", + target_os = "solaris" )))] mod imp { pub unsafe fn init() {} pub unsafe fn cleanup() {} - pub unsafe fn make_handler() -> super::Handler { + pub unsafe fn make_handler(_main_thread: bool) -> super::Handler { super::Handler::null() } diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index 4cd7c0e3059b1..7d25c974ed363 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -182,8 +182,11 @@ impl Thread { if let Some(f) = pthread_setname_np.get() { #[cfg(target_os = "nto")] - let name = truncate_cstr::<{ libc::_NTO_THREAD_NAME_MAX as usize }>(name); + const THREAD_NAME_MAX: usize = libc::_NTO_THREAD_NAME_MAX as usize; + #[cfg(any(target_os = "solaris", target_os = "illumos"))] + const THREAD_NAME_MAX: usize = 32; + let name = truncate_cstr::<{ THREAD_NAME_MAX }>(name); let res = unsafe { f(libc::pthread_self(), name.as_ptr()) }; debug_assert_eq!(res, 0); } @@ -225,9 +228,20 @@ impl Thread { // Newlib, Emscripten, and VxWorks have no way to set a thread name. } - #[cfg(target_os = "linux")] + #[cfg(any( + target_os = "linux", + target_os = "freebsd", + target_os = "netbsd", + target_os = "solaris", + target_os = "illumos" + ))] pub fn get_name() -> Option { + #[cfg(target_os = "linux")] const TASK_COMM_LEN: usize = 16; + #[cfg(target_os = "freebsd")] + const TASK_COMM_LEN: usize = libc::MAXCOMLEN + 1; + #[cfg(any(target_os = "netbsd", target_os = "solaris", target_os = "illumos"))] + const TASK_COMM_LEN: usize = 32; let mut name = vec![0u8; TASK_COMM_LEN]; let res = unsafe { libc::pthread_getname_np(libc::pthread_self(), name.as_mut_ptr().cast(), name.len()) @@ -252,12 +266,35 @@ impl Thread { CString::new(name).ok() } + #[cfg(target_os = "haiku")] + pub fn get_name() -> Option { + unsafe { + let mut tinfo = mem::MaybeUninit::::uninit(); + // See BeOS teams group and threads api. + // https://www.haiku-os.org/legacy-docs/bebook/TheKernelKit_ThreadsAndTeams_Overview.html + let thread_self = libc::find_thread(ptr::null_mut()); + let res = libc::get_thread_info(thread_self, tinfo.as_mut_ptr()); + if res != libc::B_OK { + return None; + } + let info = tinfo.assume_init(); + let name = + core::slice::from_raw_parts(info.name.as_ptr() as *const u8, info.name.len()); + CStr::from_bytes_until_nul(name).map(CStr::to_owned).ok() + } + } + #[cfg(not(any( target_os = "linux", + target_os = "freebsd", + target_os = "netbsd", target_os = "macos", target_os = "ios", target_os = "tvos", - target_os = "watchos" + target_os = "watchos", + target_os = "haiku", + target_os = "solaris", + target_os = "illumos" )))] pub fn get_name() -> Option { None @@ -335,6 +372,8 @@ impl Drop for Thread { target_os = "tvos", target_os = "watchos", target_os = "nto", + target_os = "solaris", + target_os = "illumos", ))] fn truncate_cstr(cstr: &CStr) -> [libc::c_char; MAX_WITH_NUL] { let mut result = [0; MAX_WITH_NUL]; @@ -729,302 +768,6 @@ mod cgroups { } } -#[cfg(all( - not(target_os = "linux"), - not(target_os = "freebsd"), - not(target_os = "hurd"), - not(target_os = "macos"), - not(target_os = "netbsd"), - not(target_os = "openbsd"), - not(target_os = "solaris") -))] -#[cfg_attr(test, allow(dead_code))] -pub mod guard { - use crate::ops::Range; - pub type Guard = Range; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} - -#[cfg(any( - target_os = "linux", - target_os = "freebsd", - target_os = "hurd", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris" -))] -#[cfg_attr(test, allow(dead_code))] -pub mod guard { - #[cfg(not(all(target_os = "linux", target_env = "gnu")))] - use libc::{mmap as mmap64, mprotect}; - #[cfg(all(target_os = "linux", target_env = "gnu"))] - use libc::{mmap64, mprotect}; - use libc::{MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE}; - - use crate::io; - use crate::ops::Range; - use crate::sync::atomic::{AtomicUsize, Ordering}; - use crate::sys::os; - - // This is initialized in init() and only read from after - static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0); - - pub type Guard = Range; - - #[cfg(target_os = "solaris")] - unsafe fn get_stack_start() -> Option<*mut libc::c_void> { - let mut current_stack: libc::stack_t = crate::mem::zeroed(); - assert_eq!(libc::stack_getbounds(&mut current_stack), 0); - Some(current_stack.ss_sp) - } - - #[cfg(target_os = "macos")] - unsafe fn get_stack_start() -> Option<*mut libc::c_void> { - let th = libc::pthread_self(); - let stackptr = libc::pthread_get_stackaddr_np(th); - Some(stackptr.map_addr(|addr| addr - libc::pthread_get_stacksize_np(th))) - } - - #[cfg(target_os = "openbsd")] - unsafe fn get_stack_start() -> Option<*mut libc::c_void> { - let mut current_stack: libc::stack_t = crate::mem::zeroed(); - assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(), &mut current_stack), 0); - - let stack_ptr = current_stack.ss_sp; - let stackaddr = if libc::pthread_main_np() == 1 { - // main thread - stack_ptr.addr() - current_stack.ss_size + PAGE_SIZE.load(Ordering::Relaxed) - } else { - // new thread - stack_ptr.addr() - current_stack.ss_size - }; - Some(stack_ptr.with_addr(stackaddr)) - } - - #[cfg(any( - target_os = "android", - target_os = "freebsd", - target_os = "netbsd", - target_os = "hurd", - target_os = "linux", - target_os = "l4re" - ))] - unsafe fn get_stack_start() -> Option<*mut libc::c_void> { - let mut ret = None; - let mut attr: libc::pthread_attr_t = crate::mem::zeroed(); - #[cfg(target_os = "freebsd")] - assert_eq!(libc::pthread_attr_init(&mut attr), 0); - #[cfg(target_os = "freebsd")] - let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr); - #[cfg(not(target_os = "freebsd"))] - let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr); - if e == 0 { - let mut stackaddr = crate::ptr::null_mut(); - let mut stacksize = 0; - assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize), 0); - ret = Some(stackaddr); - } - if e == 0 || cfg!(target_os = "freebsd") { - assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); - } - ret - } - - // Precondition: PAGE_SIZE is initialized. - unsafe fn get_stack_start_aligned() -> Option<*mut libc::c_void> { - let page_size = PAGE_SIZE.load(Ordering::Relaxed); - assert!(page_size != 0); - let stackptr = get_stack_start()?; - let stackaddr = stackptr.addr(); - - // Ensure stackaddr is page aligned! A parent process might - // have reset RLIMIT_STACK to be non-page aligned. The - // pthread_attr_getstack() reports the usable stack area - // stackaddr < stackaddr + stacksize, so if stackaddr is not - // page-aligned, calculate the fix such that stackaddr < - // new_page_aligned_stackaddr < stackaddr + stacksize - let remainder = stackaddr % page_size; - Some(if remainder == 0 { - stackptr - } else { - stackptr.with_addr(stackaddr + page_size - remainder) - }) - } - - pub unsafe fn init() -> Option { - let page_size = os::page_size(); - PAGE_SIZE.store(page_size, Ordering::Relaxed); - - if cfg!(all(target_os = "linux", not(target_env = "musl"))) { - // Linux doesn't allocate the whole stack right away, and - // the kernel has its own stack-guard mechanism to fault - // when growing too close to an existing mapping. If we map - // our own guard, then the kernel starts enforcing a rather - // large gap above that, rendering much of the possible - // stack space useless. See #43052. - // - // Instead, we'll just note where we expect rlimit to start - // faulting, so our handler can report "stack overflow", and - // trust that the kernel's own stack guard will work. - let stackptr = get_stack_start_aligned()?; - let stackaddr = stackptr.addr(); - Some(stackaddr - page_size..stackaddr) - } else if cfg!(all(target_os = "linux", target_env = "musl")) { - // For the main thread, the musl's pthread_attr_getstack - // returns the current stack size, rather than maximum size - // it can eventually grow to. It cannot be used to determine - // the position of kernel's stack guard. - None - } else if cfg!(target_os = "freebsd") { - // FreeBSD's stack autogrows, and optionally includes a guard page - // at the bottom. If we try to remap the bottom of the stack - // ourselves, FreeBSD's guard page moves upwards. So we'll just use - // the builtin guard page. - let stackptr = get_stack_start_aligned()?; - let guardaddr = stackptr.addr(); - // Technically the number of guard pages is tunable and controlled - // by the security.bsd.stack_guard_page sysctl. - // By default it is 1, checking once is enough since it is - // a boot time config value. - static LOCK: crate::sync::OnceLock = crate::sync::OnceLock::new(); - let guard = guardaddr - ..guardaddr - + *LOCK.get_or_init(|| { - use crate::sys::weak::dlsym; - dlsym!(fn sysctlbyname(*const libc::c_char, *mut libc::c_void, *mut libc::size_t, *const libc::c_void, libc::size_t) -> libc::c_int); - let mut guard: usize = 0; - let mut size = crate::mem::size_of_val(&guard); - let oid = crate::ffi::CStr::from_bytes_with_nul( - b"security.bsd.stack_guard_page\0", - ) - .unwrap(); - match sysctlbyname.get() { - Some(fcn) => { - if fcn(oid.as_ptr(), core::ptr::addr_of_mut!(guard) as *mut _, core::ptr::addr_of_mut!(size) as *mut _, crate::ptr::null_mut(), 0) == 0 { - return guard; - } - return 1; - }, - _ => { return 1; } - } - }) * page_size; - Some(guard) - } else if cfg!(any(target_os = "openbsd", target_os = "netbsd")) { - // OpenBSD stack already includes a guard page, and stack is - // immutable. - // NetBSD stack includes the guard page. - // - // We'll just note where we expect rlimit to start - // faulting, so our handler can report "stack overflow", and - // trust that the kernel's own stack guard will work. - let stackptr = get_stack_start_aligned()?; - let stackaddr = stackptr.addr(); - Some(stackaddr - page_size..stackaddr) - } else { - // Reallocate the last page of the stack. - // This ensures SIGBUS will be raised on - // stack overflow. - // Systems which enforce strict PAX MPROTECT do not allow - // to mprotect() a mapping with less restrictive permissions - // than the initial mmap() used, so we mmap() here with - // read/write permissions and only then mprotect() it to - // no permissions at all. See issue #50313. - let stackptr = get_stack_start_aligned()?; - let result = mmap64( - stackptr, - page_size, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANON | MAP_FIXED, - -1, - 0, - ); - if result != stackptr || result == MAP_FAILED { - panic!("failed to allocate a guard page: {}", io::Error::last_os_error()); - } - - let result = mprotect(stackptr, page_size, PROT_NONE); - if result != 0 { - panic!("failed to protect the guard page: {}", io::Error::last_os_error()); - } - - let guardaddr = stackptr.addr(); - - Some(guardaddr..guardaddr + page_size) - } - } - - #[cfg(any(target_os = "macos", target_os = "openbsd", target_os = "solaris"))] - pub unsafe fn current() -> Option { - let stackptr = get_stack_start()?; - let stackaddr = stackptr.addr(); - Some(stackaddr - PAGE_SIZE.load(Ordering::Relaxed)..stackaddr) - } - - #[cfg(any( - target_os = "android", - target_os = "freebsd", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "l4re" - ))] - pub unsafe fn current() -> Option { - let mut ret = None; - let mut attr: libc::pthread_attr_t = crate::mem::zeroed(); - #[cfg(target_os = "freebsd")] - assert_eq!(libc::pthread_attr_init(&mut attr), 0); - #[cfg(target_os = "freebsd")] - let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr); - #[cfg(not(target_os = "freebsd"))] - let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr); - if e == 0 { - let mut guardsize = 0; - assert_eq!(libc::pthread_attr_getguardsize(&attr, &mut guardsize), 0); - if guardsize == 0 { - if cfg!(all(target_os = "linux", target_env = "musl")) { - // musl versions before 1.1.19 always reported guard - // size obtained from pthread_attr_get_np as zero. - // Use page size as a fallback. - guardsize = PAGE_SIZE.load(Ordering::Relaxed); - } else { - panic!("there is no guard page"); - } - } - let mut stackptr = crate::ptr::null_mut::(); - let mut size = 0; - assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackptr, &mut size), 0); - - let stackaddr = stackptr.addr(); - ret = if cfg!(any(target_os = "freebsd", target_os = "netbsd", target_os = "hurd")) { - Some(stackaddr - guardsize..stackaddr) - } else if cfg!(all(target_os = "linux", target_env = "musl")) { - Some(stackaddr - guardsize..stackaddr) - } else if cfg!(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc"))) - { - // glibc used to include the guard area within the stack, as noted in the BUGS - // section of `man pthread_attr_getguardsize`. This has been corrected starting - // with glibc 2.27, and in some distro backports, so the guard is now placed at the - // end (below) the stack. There's no easy way for us to know which we have at - // runtime, so we'll just match any fault in the range right above or below the - // stack base to call that fault a stack overflow. - Some(stackaddr - guardsize..stackaddr + guardsize) - } else { - Some(stackaddr..stackaddr + guardsize) - }; - } - if e == 0 || cfg!(target_os = "freebsd") { - assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); - } - ret - } -} - // glibc >= 2.15 has a __pthread_get_minstack() function that returns // PTHREAD_STACK_MIN plus bytes needed for thread-local storage. // We need that information to avoid blowing up when a small stack diff --git a/library/std/src/sys/pal/unsupported/thread.rs b/library/std/src/sys/pal/unsupported/thread.rs index b3a91ee1d4cb6..d3f2fa35b9299 100644 --- a/library/std/src/sys/pal/unsupported/thread.rs +++ b/library/std/src/sys/pal/unsupported/thread.rs @@ -38,13 +38,3 @@ impl Thread { pub fn available_parallelism() -> io::Result> { unsupported() } - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/library/std/src/sys/pal/wasi/thread.rs b/library/std/src/sys/pal/wasi/thread.rs index 4b116052f8f8e..940f0c8423af6 100644 --- a/library/std/src/sys/pal/wasi/thread.rs +++ b/library/std/src/sys/pal/wasi/thread.rs @@ -193,13 +193,3 @@ impl Thread { pub fn available_parallelism() -> io::Result> { unsupported() } - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/library/std/src/sys/pal/wasip2/cabi_realloc.rs b/library/std/src/sys/pal/wasip2/cabi_realloc.rs new file mode 100644 index 0000000000000..820063173d657 --- /dev/null +++ b/library/std/src/sys/pal/wasip2/cabi_realloc.rs @@ -0,0 +1,65 @@ +//! This module contains a canonical definition of the `cabi_realloc` function +//! for the component model. +//! +//! The component model's canonical ABI for representing datatypes in memory +//! makes use of this function when transferring lists and strings, for example. +//! This function behaves like C's `realloc` but also takes alignment into +//! account. +//! +//! Components are notably not required to export this function, but nearly +//! all components end up doing so currently. This definition in the standard +//! library removes the need for all compilations to define this themselves. +//! +//! More information about the canonical ABI can be found at +//! +//! +//! Note that the name of this function is not standardized in the canonical ABI +//! at this time. Instead it's a convention of the "componentization process" +//! where a core wasm module is converted to a component to use this name. +//! Additionally this is not the only possible definition of this function, so +//! this is defined as a "weak" symbol. This means that other definitions are +//! allowed to overwrite it if they are present in a compilation. + +use crate::alloc::{self, Layout}; +use crate::ptr; + +#[used] +static FORCE_CODEGEN_OF_CABI_REALLOC: unsafe extern "C" fn( + *mut u8, + usize, + usize, + usize, +) -> *mut u8 = cabi_realloc; + +#[linkage = "weak"] +#[no_mangle] +pub unsafe extern "C" fn cabi_realloc( + old_ptr: *mut u8, + old_len: usize, + align: usize, + new_len: usize, +) -> *mut u8 { + let layout; + let ptr = if old_len == 0 { + if new_len == 0 { + return ptr::without_provenance_mut(align); + } + layout = Layout::from_size_align_unchecked(new_len, align); + alloc::alloc(layout) + } else { + debug_assert_ne!(new_len, 0, "non-zero old_len requires non-zero new_len!"); + layout = Layout::from_size_align_unchecked(old_len, align); + alloc::realloc(old_ptr, layout, new_len) + }; + if ptr.is_null() { + // Print a nice message in debug mode, but in release mode don't + // pull in so many dependencies related to printing so just emit an + // `unreachable` instruction. + if cfg!(debug_assertions) { + alloc::handle_alloc_error(layout); + } else { + super::abort_internal(); + } + } + return ptr; +} diff --git a/library/std/src/sys/pal/wasip2/mod.rs b/library/std/src/sys/pal/wasip2/mod.rs index d1d444d7b798b..94aa458d2f90e 100644 --- a/library/std/src/sys/pal/wasip2/mod.rs +++ b/library/std/src/sys/pal/wasip2/mod.rs @@ -10,8 +10,6 @@ pub mod alloc; #[path = "../wasi/args.rs"] pub mod args; -#[path = "../unix/cmath.rs"] -pub mod cmath; #[path = "../wasi/env.rs"] pub mod env; #[path = "../wasi/fd.rs"] @@ -28,10 +26,6 @@ pub mod io; pub mod net; #[path = "../wasi/os.rs"] pub mod os; -#[path = "../unix/os_str.rs"] -pub mod os_str; -#[path = "../unix/path.rs"] -pub mod path; #[path = "../unsupported/pipe.rs"] pub mod pipe; #[path = "../unsupported/process.rs"] @@ -72,3 +66,5 @@ pub use helpers::decode_error_kind; use helpers::err2io; pub use helpers::hashmap_random_keys; pub use helpers::is_interrupted; + +mod cabi_realloc; diff --git a/library/std/src/sys/pal/windows/stack_overflow.rs b/library/std/src/sys/pal/windows/stack_overflow.rs index 627763da8561b..f93f31026f818 100644 --- a/library/std/src/sys/pal/windows/stack_overflow.rs +++ b/library/std/src/sys/pal/windows/stack_overflow.rs @@ -3,21 +3,12 @@ use crate::sys::c; use crate::thread; -use super::api; - -pub struct Handler; - -impl Handler { - pub unsafe fn new() -> Handler { - // This API isn't available on XP, so don't panic in that case and just - // pray it works out ok. - if c::SetThreadStackGuarantee(&mut 0x5000) == 0 - && api::get_last_error().code != c::ERROR_CALL_NOT_IMPLEMENTED - { - panic!("failed to reserve stack space for exception handling"); - } - Handler - } +/// Reserve stack space for use in stack overflow exceptions. +pub unsafe fn reserve_stack() { + let result = c::SetThreadStackGuarantee(&mut 0x5000); + // Reserving stack space is not critical so we allow it to fail in the released build of libstd. + // We still use debug assert here so that CI will test that we haven't made a mistake calling the function. + debug_assert_ne!(result, 0, "failed to reserve stack space for exception handling"); } unsafe extern "system" fn vectored_handler(ExceptionInfo: *mut c::EXCEPTION_POINTERS) -> c::LONG { @@ -36,9 +27,10 @@ unsafe extern "system" fn vectored_handler(ExceptionInfo: *mut c::EXCEPTION_POIN } pub unsafe fn init() { - if c::AddVectoredExceptionHandler(0, Some(vectored_handler)).is_null() { - panic!("failed to install exception handler"); - } + let result = c::AddVectoredExceptionHandler(0, Some(vectored_handler)); + // Similar to the above, adding the stack overflow handler is allowed to fail + // but a debug assert is used so CI will still test that it normally works. + debug_assert!(!result.is_null(), "failed to install exception handler"); // Set the thread stack guarantee for the main thread. - let _h = Handler::new(); + reserve_stack(); } diff --git a/library/std/src/sys/pal/windows/stack_overflow_uwp.rs b/library/std/src/sys/pal/windows/stack_overflow_uwp.rs index afdf7f566ae51..9e9b3efaf1b14 100644 --- a/library/std/src/sys/pal/windows/stack_overflow_uwp.rs +++ b/library/std/src/sys/pal/windows/stack_overflow_uwp.rs @@ -1,11 +1,4 @@ #![cfg_attr(test, allow(dead_code))] -pub struct Handler; - -impl Handler { - pub fn new() -> Handler { - Handler - } -} - +pub unsafe fn reserve_stack() {} pub unsafe fn init() {} diff --git a/library/std/src/sys/pal/windows/thread.rs b/library/std/src/sys/pal/windows/thread.rs index 970bd9c6ce7e6..fe174e1e34063 100644 --- a/library/std/src/sys/pal/windows/thread.rs +++ b/library/std/src/sys/pal/windows/thread.rs @@ -48,9 +48,8 @@ impl Thread { extern "system" fn thread_start(main: *mut c_void) -> c::DWORD { unsafe { - // Next, set up our stack overflow handler which may get triggered if we run - // out of stack. - let _handler = stack_overflow::Handler::new(); + // Next, reserve some stack space for if we otherwise run out of stack. + stack_overflow::reserve_stack(); // Finally, let's run some code. Box::from_raw(main as *mut Box)(); } @@ -144,14 +143,3 @@ pub fn available_parallelism() -> io::Result> { cpus => Ok(unsafe { NonZero::new_unchecked(cpus) }), } } - -#[cfg_attr(test, allow(dead_code))] -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/library/std/src/sys/pal/xous/thread.rs b/library/std/src/sys/pal/xous/thread.rs index f95ceb7343bd1..c1fd1c0d6534a 100644 --- a/library/std/src/sys/pal/xous/thread.rs +++ b/library/std/src/sys/pal/xous/thread.rs @@ -140,13 +140,3 @@ pub fn available_parallelism() -> io::Result> { // We're unicore right now. Ok(unsafe { NonZero::new_unchecked(1) }) } - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/library/std/src/sys/pal/xous/thread_local_key.rs b/library/std/src/sys/pal/xous/thread_local_key.rs index 2aaf46d0244a7..6c29813c79dfd 100644 --- a/library/std/src/sys/pal/xous/thread_local_key.rs +++ b/library/std/src/sys/pal/xous/thread_local_key.rs @@ -49,7 +49,7 @@ fn tls_ptr_addr() -> *mut *mut u8 { out(reg) tp, ); } - core::ptr::from_exposed_addr_mut::<*mut u8>(tp) + core::ptr::with_exposed_provenance_mut::<*mut u8>(tp) } /// Create an area of memory that's unique per thread. This area will diff --git a/library/std/src/sys/pal/zkvm/thread_local_key.rs b/library/std/src/sys/pal/zkvm/thread_local_key.rs index 3ffe6247344e8..2f67924c61823 100644 --- a/library/std/src/sys/pal/zkvm/thread_local_key.rs +++ b/library/std/src/sys/pal/zkvm/thread_local_key.rs @@ -9,13 +9,13 @@ pub unsafe fn create(_dtor: Option) -> Key { #[inline] pub unsafe fn set(key: Key, value: *mut u8) { - let key: *mut *mut u8 = core::ptr::from_exposed_addr_mut(key); + let key: *mut *mut u8 = core::ptr::with_exposed_provenance_mut(key); *key = value; } #[inline] pub unsafe fn get(key: Key) -> *mut u8 { - let key: *mut *mut u8 = core::ptr::from_exposed_addr_mut(key); + let key: *mut *mut u8 = core::ptr::with_exposed_provenance_mut(key); *key } diff --git a/library/std/src/sys/personality/dwarf/eh.rs b/library/std/src/sys/personality/dwarf/eh.rs index a78084de0faef..3f3615ea3e0a4 100644 --- a/library/std/src/sys/personality/dwarf/eh.rs +++ b/library/std/src/sys/personality/dwarf/eh.rs @@ -125,7 +125,7 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result // Can never have null landing pad for sjlj -- that would have // been indicated by a -1 call site index. // FIXME(strict provenance) - let lpad = ptr::from_exposed_addr((cs_lpad + 1) as usize); + let lpad = ptr::with_exposed_provenance((cs_lpad + 1) as usize); return Ok(interpret_cs_action(action_table, cs_action_entry, lpad)); } } diff --git a/library/std/src/sys/sync/rwlock/queue.rs b/library/std/src/sys/sync/rwlock/queue.rs index dce966086b8ff..d1918855797ad 100644 --- a/library/std/src/sys/sync/rwlock/queue.rs +++ b/library/std/src/sys/sync/rwlock/queue.rs @@ -115,8 +115,7 @@ use crate::sync::atomic::{ AtomicBool, AtomicPtr, Ordering::{AcqRel, Acquire, Relaxed, Release}, }; -use crate::sys_common::thread_info; -use crate::thread::Thread; +use crate::thread::{self, Thread}; // Locking uses exponential backoff. `SPIN_COUNT` indicates how many times the // locking operation will be retried. @@ -203,8 +202,7 @@ impl Node { fn prepare(&mut self) { // Fall back to creating an unnamed `Thread` handle to allow locking in // TLS destructors. - self.thread - .get_or_init(|| thread_info::current_thread().unwrap_or_else(|| Thread::new(None))); + self.thread.get_or_init(|| thread::try_current().unwrap_or_else(|| Thread::new(None))); self.completed = AtomicBool::new(false); } diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index 5410f135a73f1..5abf201aa201b 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -26,7 +26,6 @@ pub mod io; pub mod lazy_box; pub mod process; pub mod thread; -pub mod thread_info; pub mod thread_local_dtor; pub mod thread_parking; pub mod wstr; diff --git a/library/std/src/sys_common/thread_info.rs b/library/std/src/sys_common/thread_info.rs deleted file mode 100644 index ec1428ea40ec6..0000000000000 --- a/library/std/src/sys_common/thread_info.rs +++ /dev/null @@ -1,53 +0,0 @@ -#![allow(dead_code)] // stack_guard isn't used right now on all platforms - -use crate::cell::OnceCell; -use crate::sys; -use crate::sys::thread::guard::Guard; -use crate::thread::Thread; - -struct ThreadInfo { - stack_guard: OnceCell, - thread: OnceCell, -} - -thread_local! { - static THREAD_INFO: ThreadInfo = const { ThreadInfo { - stack_guard: OnceCell::new(), - thread: OnceCell::new() - } }; -} - -impl ThreadInfo { - fn with(f: F) -> Option - where - F: FnOnce(&Thread, &OnceCell) -> R, - { - THREAD_INFO - .try_with(move |thread_info| { - let thread = - thread_info.thread.get_or_init(|| Thread::new(sys::thread::Thread::get_name())); - f(thread, &thread_info.stack_guard) - }) - .ok() - } -} - -pub fn current_thread() -> Option { - ThreadInfo::with(|thread, _| thread.clone()) -} - -pub fn stack_guard() -> Option { - ThreadInfo::with(|_, guard| guard.get().cloned()).flatten() -} - -/// Set new thread info, panicking if it has already been initialized -#[allow(unreachable_code, unreachable_patterns)] // some platforms don't use stack_guard -pub fn set(stack_guard: Option, thread: Thread) { - THREAD_INFO.with(move |thread_info| { - rtassert!(thread_info.stack_guard.get().is_none() && thread_info.thread.get().is_none()); - if let Some(guard) = stack_guard { - thread_info.stack_guard.set(guard).unwrap(); - } - thread_info.thread.set(thread).unwrap(); - }); -} diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 85de2980133d5..f7eb92bc61e2f 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -159,7 +159,7 @@ mod tests; use crate::any::Any; -use crate::cell::UnsafeCell; +use crate::cell::{OnceCell, UnsafeCell}; use crate::ffi::{CStr, CString}; use crate::fmt; use crate::io; @@ -174,7 +174,6 @@ use crate::str; use crate::sync::Arc; use crate::sys::thread as imp; use crate::sys_common::thread; -use crate::sys_common::thread_info; use crate::sys_common::thread_parking::Parker; use crate::sys_common::{AsInner, IntoInner}; use crate::time::{Duration, Instant}; @@ -518,12 +517,8 @@ impl Builder { crate::io::set_output_capture(output_capture); - // SAFETY: we constructed `f` initialized. let f = f.into_inner(); - // SAFETY: the stack guard passed is the one for the current thread. - // This means the current thread's stack and the new thread's stack - // are properly set and protected from each other. - thread_info::set(unsafe { imp::guard::current() }, their_thread); + set_current(their_thread); let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| { crate::sys_common::backtrace::__rust_begin_short_backtrace(f) })); @@ -683,6 +678,27 @@ where Builder::new().spawn(f).expect("failed to spawn thread") } +thread_local! { + static CURRENT: OnceCell = const { OnceCell::new() }; +} + +/// Sets the thread handle for the current thread. +/// +/// Panics if the handle has been set already or when called from a TLS destructor. +pub(crate) fn set_current(thread: Thread) { + CURRENT.with(|current| current.set(thread).unwrap()); +} + +/// Gets a handle to the thread that invokes it. +/// +/// In contrast to the public `current` function, this will not panic if called +/// from inside a TLS destructor. +pub(crate) fn try_current() -> Option { + CURRENT + .try_with(|current| current.get_or_init(|| Thread::new(imp::Thread::get_name())).clone()) + .ok() +} + /// Gets a handle to the thread that invokes it. /// /// # Examples @@ -705,7 +721,7 @@ where #[must_use] #[stable(feature = "rust1", since = "1.0.0")] pub fn current() -> Thread { - thread_info::current_thread().expect( + try_current().expect( "use of std::thread::current() is not possible \ after the thread's local data has been destroyed", ) diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 30d728aa23005..39add60e705fe 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -1038,6 +1038,7 @@ def check_vendored_status(self): sync_dirs = "--sync ./src/tools/cargo/Cargo.toml " \ "--sync ./src/tools/rust-analyzer/Cargo.toml " \ "--sync ./compiler/rustc_codegen_cranelift/Cargo.toml " \ + "--sync ./compiler/rustc_codegen_gcc/Cargo.toml " \ "--sync ./src/bootstrap/Cargo.toml " eprint('ERROR: vendoring required, but vendor directory does not exist.') eprint(' Run `cargo vendor {}` to initialize the ' diff --git a/src/bootstrap/src/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs index 74a924d86c796..4b182a7a693dd 100644 --- a/src/bootstrap/src/bin/rustc.rs +++ b/src/bootstrap/src/bin/rustc.rs @@ -150,7 +150,7 @@ fn main() { { cmd.arg("-Ztls-model=initial-exec"); } - } else if std::env::var("MIRI").is_err() { + } else { // Find any host flags that were passed by bootstrap. // The flags are stored in a RUSTC_HOST_FLAGS variable, separated by spaces. if let Ok(flags) = std::env::var("RUSTC_HOST_FLAGS") { @@ -220,6 +220,12 @@ fn main() { } } + if env::var_os("RUSTC_BOLT_LINK_FLAGS").is_some() { + if let Some("rustc_driver") = crate_name { + cmd.arg("-Clink-args=-Wl,-q"); + } + } + let is_test = args.iter().any(|a| a == "--test"); if verbose > 2 { let rust_env_vars = @@ -244,12 +250,6 @@ fn main() { eprintln!("{prefix} libdir: {libdir:?}"); } - if env::var_os("RUSTC_BOLT_LINK_FLAGS").is_some() { - if let Some("rustc_driver") = crate_name { - cmd.arg("-Clink-args=-Wl,-q"); - } - } - bin_helpers::maybe_dump(format!("stage{stage}-rustc"), &cmd); let start = Instant::now(); diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index d40a3ea4c884e..e03997181ee5d 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -643,13 +643,13 @@ impl Step for StdLink { t!(fs::create_dir_all(&sysroot_bin_dir)); builder.cp_link_r(&stage0_bin_dir, &sysroot_bin_dir); - // Copy all *.so files from stage0/lib to stage0-sysroot/lib + // Copy all files from stage0/lib to stage0-sysroot/lib let stage0_lib_dir = builder.out.join(host).join("stage0/lib"); if let Ok(files) = fs::read_dir(stage0_lib_dir) { for file in files { let file = t!(file); let path = file.path(); - if path.is_file() && is_dylib(&file.file_name().into_string().unwrap()) { + if path.is_file() { builder .copy_link(&path, &sysroot.join("lib").join(path.file_name().unwrap())); } @@ -825,6 +825,7 @@ fn cp_rustc_component_to_ci_sysroot( #[derive(Debug, PartialOrd, Ord, Clone, PartialEq, Eq, Hash)] pub struct Rustc { pub target: TargetSelection, + /// The **previous** compiler used to compile this compiler. pub compiler: Compiler, /// Whether to build a subset of crates, rather than the whole compiler. /// @@ -1130,12 +1131,6 @@ pub fn rustc_cargo_env( cargo.env("CFG_DEFAULT_LINKER", s); } - if builder.config.rustc_parallel { - // keep in sync with `bootstrap/lib.rs:Build::rustc_features` - // `cfg` option for rustc, `features` option for cargo, for conditional compilation - cargo.rustflag("--cfg=parallel_compiler"); - cargo.rustdocflag("--cfg=parallel_compiler"); - } if builder.config.rust_verify_llvm_ir { cargo.env("RUSTC_VERIFY_LLVM_IR", "1"); } @@ -1518,12 +1513,9 @@ impl Step for Sysroot { run.never() } - /// Returns the sysroot for the `compiler` specified that *this build system - /// generates*. - /// - /// That is, the sysroot for the stage0 compiler is not what the compiler - /// thinks it is by default, but it's the same as the default for stages - /// 1-3. + /// Returns the sysroot that `compiler` is supposed to use. + /// For the stage0 compiler, this is stage0-sysroot (because of the initial std build). + /// For all other stages, it's the same stage directory that the compiler lives in. fn run(self, builder: &Builder<'_>) -> PathBuf { let compiler = self.compiler; let host_dir = builder.out.join(compiler.host.triple); diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index d9c7032d0db8e..b51d3e157887b 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -995,9 +995,9 @@ impl Step for PlainSourceTarball { if builder.rust_info().is_managed_git_subrepository() || builder.rust_info().is_from_tarball() { - if builder.rust_info().is_managed_git_subrepository() { - // Ensure we have the submodules checked out. - builder.update_submodule(Path::new("src/tools/cargo")); + // Ensure we have all submodules from src and other directories checked out. + for submodule in builder.get_all_submodules() { + builder.update_submodule(Path::new(submodule)); } // Vendor all Cargo dependencies @@ -1011,6 +1011,8 @@ impl Step for PlainSourceTarball { .arg("--sync") .arg(builder.src.join("./compiler/rustc_codegen_cranelift/Cargo.toml")) .arg("--sync") + .arg(builder.src.join("./compiler/rustc_codegen_gcc/Cargo.toml")) + .arg("--sync") .arg(builder.src.join("./src/bootstrap/Cargo.toml")) // Will read the libstd Cargo.toml // which uses the unstable `public-dependency` feature. @@ -1028,6 +1030,20 @@ impl Step for PlainSourceTarball { builder.create(&cargo_config_dir.join("config.toml"), &config); } + // Delete extraneous directories + // FIXME: if we're managed by git, we should probably instead ask git if the given path + // is managed by it? + for entry in walkdir::WalkDir::new(tarball.image_dir()) + .follow_links(true) + .into_iter() + .filter_map(|e| e.ok()) + { + if entry.path().is_dir() && entry.path().file_name() == Some(OsStr::new("__pycache__")) + { + t!(fs::remove_dir_all(entry.path())); + } + } + tarball.bare() } } diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 3da927b5fa0fa..e461e11677fab 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -274,6 +274,7 @@ impl Step for Llvm { target.to_string() }; + // If LLVM has already been built or been downloaded through download-ci-llvm, we avoid building it again. let Meta { stamp, res, out_dir, root } = match prebuilt_llvm_config(builder, target) { Ok(p) => return p, Err(m) => m, diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index 61ee2fc1f6f3d..7028bffea5442 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -121,8 +121,6 @@ impl Step for ReplaceVersionPlaceholder { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Miri { - stage: u32, - host: TargetSelection, target: TargetSelection, } @@ -135,29 +133,35 @@ impl Step for Miri { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Miri { - stage: run.builder.top_stage, - host: run.build_triple(), - target: run.target, - }); + run.builder.ensure(Miri { target: run.target }); } fn run(self, builder: &Builder<'_>) { - let stage = self.stage; - let host = self.host; + let host = builder.build.build; let target = self.target; - let compiler = builder.compiler(stage, host); - - let miri = - builder.ensure(tool::Miri { compiler, target: self.host, extra_features: Vec::new() }); - let miri_sysroot = test::Miri::build_miri_sysroot(builder, compiler, &miri, target); + let stage = builder.top_stage; + if stage == 0 { + eprintln!("miri cannot be run at stage 0"); + std::process::exit(1); + } + + // This compiler runs on the host, we'll just use it for the target. + let target_compiler = builder.compiler(stage, host); + // Similar to `compile::Assemble`, build with the previous stage's compiler. Otherwise + // we'd have stageN/bin/rustc and stageN/bin/rustdoc be effectively different stage + // compilers, which isn't what we want. Rustdoc should be linked in the same way as the + // rustc compiler it's paired with, so it must be built with the previous stage compiler. + let host_compiler = builder.compiler(stage - 1, host); + + // Get a target sysroot for Miri. + let miri_sysroot = test::Miri::build_miri_sysroot(builder, target_compiler, target); // # Run miri. // Running it via `cargo run` as that figures out the right dylib path. // add_rustc_lib_path does not add the path that contains librustc_driver-<...>.so. let mut miri = tool::prepare_tool_cargo( builder, - compiler, + host_compiler, Mode::ToolRustc, host, "run", diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 48596af7ca029..bacf5f0d33cec 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -493,8 +493,6 @@ impl Step for RustDemangler { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Miri { - stage: u32, - host: TargetSelection, target: TargetSelection, } @@ -503,41 +501,26 @@ impl Miri { pub fn build_miri_sysroot( builder: &Builder<'_>, compiler: Compiler, - miri: &Path, target: TargetSelection, ) -> String { let miri_sysroot = builder.out.join(compiler.host.triple).join("miri-sysroot"); - let mut cargo = tool::prepare_tool_cargo( + let mut cargo = builder::Cargo::new( builder, compiler, - Mode::ToolRustc, - compiler.host, - "run", - "src/tools/miri/cargo-miri", - SourceType::InTree, - &[], + Mode::Std, + SourceType::Submodule, + target, + "miri-setup", ); - cargo.add_rustc_lib_path(builder); - cargo.arg("--").arg("miri").arg("setup"); - cargo.arg("--target").arg(target.rustc_target_arg()); // Tell `cargo miri setup` where to find the sources. cargo.env("MIRI_LIB_SRC", builder.src.join("library")); - // Tell it where to find Miri. - cargo.env("MIRI", miri); // Tell it where to put the sysroot. cargo.env("MIRI_SYSROOT", &miri_sysroot); - // Debug things. - cargo.env("RUST_BACKTRACE", "1"); let mut cargo = Command::from(cargo); - let _guard = builder.msg( - Kind::Build, - compiler.stage + 1, - "miri sysroot", - compiler.host, - compiler.host, - ); + let _guard = + builder.msg(Kind::Build, compiler.stage, "miri sysroot", compiler.host, target); builder.run(&mut cargo); // # Determine where Miri put its sysroot. @@ -574,41 +557,51 @@ impl Step for Miri { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Miri { - stage: run.builder.top_stage, - host: run.build_triple(), - target: run.target, - }); + run.builder.ensure(Miri { target: run.target }); } /// Runs `cargo test` for miri. fn run(self, builder: &Builder<'_>) { - let stage = self.stage; - let host = self.host; + let host = builder.build.build; let target = self.target; - let compiler = builder.compiler(stage, host); - // We need the stdlib for the *next* stage, as it was built with this compiler that also built Miri. - // Except if we are at stage 2, the bootstrap loop is complete and we can stick with our current stage. - let compiler_std = builder.compiler(if stage < 2 { stage + 1 } else { stage }, host); - - let miri = - builder.ensure(tool::Miri { compiler, target: self.host, extra_features: Vec::new() }); - let _cargo_miri = builder.ensure(tool::CargoMiri { - compiler, - target: self.host, + let stage = builder.top_stage; + if stage == 0 { + eprintln!("miri cannot be tested at stage 0"); + std::process::exit(1); + } + + // This compiler runs on the host, we'll just use it for the target. + let target_compiler = builder.compiler(stage, host); + // Similar to `compile::Assemble`, build with the previous stage's compiler. Otherwise + // we'd have stageN/bin/rustc and stageN/bin/rustdoc be effectively different stage + // compilers, which isn't what we want. Rustdoc should be linked in the same way as the + // rustc compiler it's paired with, so it must be built with the previous stage compiler. + let host_compiler = builder.compiler(stage - 1, host); + + // Build our tools. + let miri = builder.ensure(tool::Miri { + compiler: host_compiler, + target: host, extra_features: Vec::new(), }); - // The stdlib we need might be at a different stage. And just asking for the - // sysroot does not seem to populate it, so we do that first. - builder.ensure(compile::Std::new(compiler_std, host)); - let sysroot = builder.sysroot(compiler_std); - // We also need a Miri sysroot. - let miri_sysroot = Miri::build_miri_sysroot(builder, compiler, &miri, target); + // the ui tests also assume cargo-miri has been built + builder.ensure(tool::CargoMiri { + compiler: host_compiler, + target: host, + extra_features: Vec::new(), + }); + + // We also need sysroots, for Miri and for the host (the latter for build scripts). + // This is for the tests so everything is done with the target compiler. + let miri_sysroot = Miri::build_miri_sysroot(builder, target_compiler, target); + builder.ensure(compile::Std::new(target_compiler, host)); + let host_sysroot = builder.sysroot(target_compiler); // # Run `cargo test`. + // This is with the Miri crate, so it uses the host compiler. let mut cargo = tool::prepare_tool_cargo( builder, - compiler, + host_compiler, Mode::ToolRustc, host, "test", @@ -616,26 +609,23 @@ impl Step for Miri { SourceType::InTree, &[], ); - let _guard = builder.msg_sysroot_tool(Kind::Test, compiler.stage, "miri", host, target); cargo.add_rustc_lib_path(builder); + // We can NOT use `run_cargo_test` since Miri's integration tests do not use the usual test + // harness and therefore do not understand the flags added by `add_flags_and_try_run_test`. + let mut cargo = prepare_cargo_test(cargo, &[], &[], "miri", host_compiler, host, builder); + // miri tests need to know about the stage sysroot cargo.env("MIRI_SYSROOT", &miri_sysroot); - cargo.env("MIRI_HOST_SYSROOT", &sysroot); + cargo.env("MIRI_HOST_SYSROOT", &host_sysroot); cargo.env("MIRI", &miri); - if builder.config.locked_deps { - // enforce lockfiles - cargo.env("CARGO_EXTRA_FLAGS", "--locked"); - } // Set the target. cargo.env("MIRI_TEST_TARGET", target.rustc_target_arg()); - // This can NOT be `run_cargo_test` since the Miri test runner - // does not understand the flags added by `add_flags_and_try_run_test`. - let mut cargo = prepare_cargo_test(cargo, &[], &[], "miri", compiler, target, builder); { + let _guard = builder.msg_sysroot_tool(Kind::Test, stage, "miri", host, target); let _time = helpers::timeit(builder); builder.run(&mut cargo); } @@ -650,8 +640,14 @@ impl Step for Miri { // Optimizations can change error locations and remove UB so don't run `fail` tests. cargo.args(["tests/pass", "tests/panic"]); - let mut cargo = prepare_cargo_test(cargo, &[], &[], "miri", compiler, target, builder); { + let _guard = builder.msg_sysroot_tool( + Kind::Test, + stage, + "miri (mir-opt-level 4)", + host, + target, + ); let _time = helpers::timeit(builder); builder.run(&mut cargo); } @@ -660,42 +656,36 @@ impl Step for Miri { // # Run `cargo miri test`. // This is just a smoke test (Miri's own CI invokes this in a bunch of different ways and ensures // that we get the desired output), but that is sufficient to make sure that the libtest harness - // itself executes properly under Miri. + // itself executes properly under Miri, and that all the logic in `cargo-miri` does not explode. + // This is running the build `cargo-miri` for the given target, so we need the target compiler. let mut cargo = tool::prepare_tool_cargo( builder, - compiler, - Mode::ToolRustc, - host, - "run", - "src/tools/miri/cargo-miri", + target_compiler, + Mode::ToolStd, // it's unclear what to use here, we're not building anything just doing a smoke test! + target, + "miri-test", + "src/tools/miri/test-cargo-miri", SourceType::Submodule, &[], ); - cargo.add_rustc_lib_path(builder); - cargo.arg("--").arg("miri").arg("test"); - if builder.config.locked_deps { - cargo.arg("--locked"); - } - cargo - .arg("--manifest-path") - .arg(builder.src.join("src/tools/miri/test-cargo-miri/Cargo.toml")); - cargo.arg("--target").arg(target.rustc_target_arg()); - cargo.arg("--").args(builder.config.test_args()); - - // `prepare_tool_cargo` sets RUSTDOC to the bootstrap wrapper and RUSTDOC_REAL to a dummy path as this is a "run", not a "test". - // Also, we want the rustdoc from the "next" stage for the same reason that we build a std from the next stage. - // So let's just set that here, and bypass bootstrap's RUSTDOC (just like cargo-miri already ignores bootstrap's RUSTC_WRAPPER). - cargo.env("RUSTDOC", builder.rustdoc(compiler_std)); - // Tell `cargo miri` where to find things. - cargo.env("MIRI_SYSROOT", &miri_sysroot); - cargo.env("MIRI_HOST_SYSROOT", sysroot); - cargo.env("MIRI", &miri); - // Debug things. - cargo.env("RUST_BACKTRACE", "1"); + // We're not using `prepare_cargo_test` so we have to do this ourselves. + // (We're not using that as the test-cargo-miri crate is not known to bootstrap.) + match builder.doc_tests { + DocTests::Yes => {} + DocTests::No => { + cargo.args(["--lib", "--bins", "--examples", "--tests", "--benches"]); + } + DocTests::Only => { + cargo.arg("--doc"); + } + } + // Finally, pass test-args and run everything. + cargo.arg("--").args(builder.config.test_args()); let mut cargo = Command::from(cargo); { + let _guard = builder.msg_sysroot_tool(Kind::Test, stage, "cargo-miri", host, target); let _time = helpers::timeit(builder); builder.run(&mut cargo); } @@ -2546,9 +2536,14 @@ fn prepare_cargo_test( // // Note that to run the compiler we need to run with the *host* libraries, // but our wrapper scripts arrange for that to be the case anyway. - let mut dylib_path = dylib_path(); - dylib_path.insert(0, PathBuf::from(&*builder.sysroot_libdir(compiler, target))); - cargo.env(dylib_path_var(), env::join_paths(&dylib_path).unwrap()); + // + // We skip everything on Miri as then this overwrites the libdir set up + // by `Cargo::new` and that actually makes things go wrong. + if builder.kind != Kind::Miri { + let mut dylib_path = dylib_path(); + dylib_path.insert(0, PathBuf::from(&*builder.sysroot_libdir(compiler, target))); + cargo.env(dylib_path_var(), env::join_paths(&dylib_path).unwrap()); + } if builder.remote_tested(target) { cargo.env( @@ -2604,28 +2599,62 @@ impl Step for Crate { let target = self.target; let mode = self.mode; + // Prepare sysroot // See [field@compile::Std::force_recompile]. builder.ensure(compile::Std::force_recompile(compiler, compiler.host)); - if builder.config.build != target { - builder.ensure(compile::Std::force_recompile(compiler, target)); - builder.ensure(RemoteCopyLibs { compiler, target }); - } - // If we're not doing a full bootstrap but we're testing a stage2 // version of libstd, then what we're actually testing is the libstd // produced in stage1. Reflect that here by updating the compiler that // we're working with automatically. let compiler = builder.compiler_for(compiler.stage, compiler.host, target); - let mut cargo = builder::Cargo::new( - builder, - compiler, - mode, - SourceType::InTree, - target, - builder.kind.as_str(), - ); + let mut cargo = if builder.kind == Kind::Miri { + if builder.top_stage == 0 { + eprintln!("ERROR: `x.py miri` requires stage 1 or higher"); + std::process::exit(1); + } + + // Build `cargo miri test` command + // (Implicitly prepares target sysroot) + let mut cargo = builder::Cargo::new( + builder, + compiler, + mode, + SourceType::InTree, + target, + "miri-test", + ); + // This hack helps bootstrap run standard library tests in Miri. The issue is as + // follows: when running `cargo miri test` on libcore, cargo builds a local copy of core + // and makes it a dependency of the integration test crate. This copy duplicates all the + // lang items, so the build fails. (Regular testing avoids this because the sysroot is a + // literal copy of what `cargo build` produces, but since Miri builds its own sysroot + // this does not work for us.) So we need to make it so that the locally built libcore + // contains all the items from `core`, but does not re-define them -- we want to replace + // the entire crate but a re-export of the sysroot crate. We do this by swapping out the + // source file: if `MIRI_REPLACE_LIBRS_IF_NOT_TEST` is set and we are building a + // `lib.rs` file, and a `lib.miri.rs` file exists in the same folder, we build that + // instead. But crucially we only do that for the library, not the test builds. + cargo.env("MIRI_REPLACE_LIBRS_IF_NOT_TEST", "1"); + cargo + } else { + // Also prepare a sysroot for the target. + if builder.config.build != target { + builder.ensure(compile::Std::force_recompile(compiler, target)); + builder.ensure(RemoteCopyLibs { compiler, target }); + } + + // Build `cargo test` command + builder::Cargo::new( + builder, + compiler, + mode, + SourceType::InTree, + target, + builder.kind.as_str(), + ) + }; match mode { Mode::Std => { diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index deab3fce54ca7..1edf65d28b76e 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -469,7 +469,7 @@ impl Step for Rustdoc { features.push("jemalloc".to_string()); } - let mut cargo = prepare_tool_cargo( + let cargo = prepare_tool_cargo( builder, build_compiler, Mode::ToolRustc, @@ -480,10 +480,6 @@ impl Step for Rustdoc { features.as_slice(), ); - if builder.config.rustc_parallel { - cargo.rustflag("--cfg=parallel_compiler"); - } - let _guard = builder.msg_tool( Mode::ToolRustc, "rustdoc", @@ -732,7 +728,7 @@ impl Step for LlvmBitcodeLinker { builder.ensure(compile::Std::new(self.compiler, self.compiler.host)); builder.ensure(compile::Rustc::new(self.compiler, self.target)); - let mut cargo = prepare_tool_cargo( + let cargo = prepare_tool_cargo( builder, self.compiler, Mode::ToolRustc, @@ -743,10 +739,6 @@ impl Step for LlvmBitcodeLinker { &self.extra_features, ); - if builder.config.rustc_parallel { - cargo.rustflag("--cfg=parallel_compiler"); - } - builder.run(&mut cargo.into()); let tool_out = builder diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 7f93fdc72ef08..9555be481e695 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -554,29 +554,7 @@ impl<'a> ShouldRun<'a> { /// /// [`path`]: ShouldRun::path pub fn paths(mut self, paths: &[&str]) -> Self { - static SUBMODULES_PATHS: OnceLock> = OnceLock::new(); - - let init_submodules_paths = |src: &PathBuf| { - let file = File::open(src.join(".gitmodules")).unwrap(); - - let mut submodules_paths = vec![]; - for line in BufReader::new(file).lines() { - if let Ok(line) = line { - let line = line.trim(); - - if line.starts_with("path") { - let actual_path = - line.split(' ').last().expect("Couldn't get value of path"); - submodules_paths.push(actual_path.to_owned()); - } - } - } - - submodules_paths - }; - - let submodules_paths = - SUBMODULES_PATHS.get_or_init(|| init_submodules_paths(&self.builder.src)); + let submodules_paths = self.builder.get_all_submodules(); self.paths.insert(PathSet::Set( paths @@ -653,6 +631,7 @@ pub enum Kind { Format, #[value(alias = "t")] Test, + Miri, Bench, #[value(alias = "d")] Doc, @@ -695,6 +674,7 @@ impl Kind { Kind::Fix => "fix", Kind::Format => "fmt", Kind::Test => "test", + Kind::Miri => "miri", Kind::Bench => "bench", Kind::Doc => "doc", Kind::Clean => "clean", @@ -844,6 +824,7 @@ impl<'a> Builder<'a> { // Run run-make last, since these won't pass without make on Windows test::RunMake, ), + Kind::Miri => describe!(test::Crate), Kind::Bench => describe!(test::Crate, test::CrateLibrustc), Kind::Doc => describe!( doc::UnstableBook, @@ -992,6 +973,7 @@ impl<'a> Builder<'a> { Subcommand::Fix => (Kind::Fix, &paths[..]), Subcommand::Doc { .. } => (Kind::Doc, &paths[..]), Subcommand::Test { .. } => (Kind::Test, &paths[..]), + Subcommand::Miri { .. } => (Kind::Miri, &paths[..]), Subcommand::Bench { .. } => (Kind::Bench, &paths[..]), Subcommand::Dist => (Kind::Dist, &paths[..]), Subcommand::Install => (Kind::Install, &paths[..]), @@ -1031,10 +1013,10 @@ impl<'a> Builder<'a> { StepDescription::run(v, self, paths); } - /// Obtain a compiler at a given stage and for a given host. Explicitly does - /// not take `Compiler` since all `Compiler` instances are meant to be - /// obtained through this function, since it ensures that they are valid - /// (i.e., built and assembled). + /// Obtain a compiler at a given stage and for a given host (i.e., this is the target that the + /// compiler will run on, *not* the target it will build code for). Explicitly does not take + /// `Compiler` since all `Compiler` instances are meant to be obtained through this function, + /// since it ensures that they are valid (i.e., built and assembled). pub fn compiler(&self, stage: u32, host: TargetSelection) -> Compiler { self.ensure(compile::Assemble { target_compiler: Compiler { stage, host } }) } @@ -1216,20 +1198,11 @@ impl<'a> Builder<'a> { } pub fn cargo_clippy_cmd(&self, run_compiler: Compiler) -> Command { - let initial_sysroot_bin = self.initial_rustc.parent().unwrap(); - // Set PATH to include the sysroot bin dir so clippy can find cargo. - // FIXME: once rust-clippy#11944 lands on beta, set `CARGO` directly instead. - let path = t!(env::join_paths( - // The sysroot comes first in PATH to avoid using rustup's cargo. - std::iter::once(PathBuf::from(initial_sysroot_bin)) - .chain(env::split_paths(&t!(env::var("PATH")))) - )); - if run_compiler.stage == 0 { // `ensure(Clippy { stage: 0 })` *builds* clippy with stage0, it doesn't use the beta clippy. let cargo_clippy = self.build.config.download_clippy(); let mut cmd = Command::new(cargo_clippy); - cmd.env("PATH", &path); + cmd.env("CARGO", &self.initial_cargo); return cmd; } @@ -1249,7 +1222,38 @@ impl<'a> Builder<'a> { let mut cmd = Command::new(cargo_clippy); cmd.env(helpers::dylib_path_var(), env::join_paths(&dylib_path).unwrap()); - cmd.env("PATH", path); + cmd.env("CARGO", &self.initial_cargo); + cmd + } + + pub fn cargo_miri_cmd(&self, run_compiler: Compiler) -> Command { + assert!(run_compiler.stage > 0, "miri can not be invoked at stage 0"); + let build_compiler = self.compiler(run_compiler.stage - 1, self.build.build); + + // Prepare the tools + let miri = self.ensure(tool::Miri { + compiler: build_compiler, + target: self.build.build, + extra_features: Vec::new(), + }); + let cargo_miri = self.ensure(tool::CargoMiri { + compiler: build_compiler, + target: self.build.build, + extra_features: Vec::new(), + }); + // Invoke cargo-miri, make sure it can find miri and cargo. + let mut cmd = Command::new(cargo_miri); + cmd.env("MIRI", &miri); + cmd.env("CARGO", &self.initial_cargo); + // Need to add the `run_compiler` libs. Those are the libs produces *by* `build_compiler`, + // so they match the Miri we just built. However this means they are actually living one + // stage up, i.e. we are running `stage0-tools-bin/miri` with the libraries in `stage1/lib`. + // This is an unfortunate off-by-1 caused (possibly) by the fact that Miri doesn't have an + // "assemble" step like rustc does that would cross the stage boundary. We can't use + // `add_rustc_lib_path` as that's a NOP on Windows but we do need these libraries added to + // the PATH due to the stage mismatch. + // Also see https://github.com/rust-lang/rust/pull/123192#issuecomment-2028901503. + add_dylib_path(self.rustc_lib_paths(run_compiler), &mut cmd); cmd } @@ -1294,20 +1298,30 @@ impl<'a> Builder<'a> { compiler: Compiler, mode: Mode, target: TargetSelection, - cmd: &str, + cmd: &str, // FIXME make this properly typed ) -> Command { - let mut cargo = if cmd == "clippy" { - self.cargo_clippy_cmd(compiler) + let mut cargo; + if cmd == "clippy" { + cargo = self.cargo_clippy_cmd(compiler); + cargo.arg(cmd); + } else if let Some(subcmd) = cmd.strip_prefix("miri") { + // Command must be "miri-X". + let subcmd = subcmd + .strip_prefix("-") + .unwrap_or_else(|| panic!("expected `miri-$subcommand`, but got {}", cmd)); + cargo = self.cargo_miri_cmd(compiler); + cargo.arg("miri").arg(subcmd); } else { - Command::new(&self.initial_cargo) - }; + cargo = Command::new(&self.initial_cargo); + cargo.arg(cmd); + } // Run cargo from the source root so it can find .cargo/config. // This matters when using vendoring and the working directory is outside the repository. cargo.current_dir(&self.src); let out_dir = self.stage_out(compiler, mode); - cargo.env("CARGO_TARGET_DIR", &out_dir).arg(cmd); + cargo.env("CARGO_TARGET_DIR", &out_dir); // Found with `rg "init_env_logger\("`. If anyone uses `init_env_logger` // from out of tree it shouldn't matter, since x.py is only used for @@ -1337,7 +1351,8 @@ impl<'a> Builder<'a> { if self.config.rust_optimize.is_release() { // FIXME: cargo bench/install do not accept `--release` - if cmd != "bench" && cmd != "install" { + // and miri doesn't want it + if cmd != "bench" && cmd != "install" && !cmd.starts_with("miri-") { cargo.arg("--release"); } } @@ -1353,14 +1368,15 @@ impl<'a> Builder<'a> { /// Cargo. This cargo will be configured to use `compiler` as the actual /// rustc compiler, its output will be scoped by `mode`'s output directory, /// it will pass the `--target` flag for the specified `target`, and will be - /// executing the Cargo command `cmd`. + /// executing the Cargo command `cmd`. `cmd` can be `miri-cmd` for commands + /// to be run with Miri. fn cargo( &self, compiler: Compiler, mode: Mode, source_type: SourceType, target: TargetSelection, - cmd: &str, + cmd: &str, // FIXME make this properly typed ) -> Cargo { let mut cargo = self.bare_cargo(compiler, mode, target, cmd); let out_dir = self.stage_out(compiler, mode); @@ -1671,7 +1687,8 @@ impl<'a> Builder<'a> { .env("RUSTDOC", self.bootstrap_out.join("rustdoc")) .env( "RUSTDOC_REAL", - if cmd == "doc" || cmd == "rustdoc" || (cmd == "test" && want_rustdoc) { + // Make sure to handle both `test` and `miri-test` commands. + if cmd == "doc" || cmd == "rustdoc" || (cmd.ends_with("test") && want_rustdoc) { self.rustdoc(compiler) } else { PathBuf::from("/path/to/nowhere/rustdoc/not/required") @@ -1694,6 +1711,15 @@ impl<'a> Builder<'a> { cargo.env("RUSTC_WRAPPER_REAL", existing_wrapper); } + // If this is for `miri-test`, prepare the sysroots. + if cmd == "miri-test" { + self.ensure(compile::Std::new(compiler, compiler.host)); + let host_sysroot = self.sysroot(compiler); + let miri_sysroot = test::Miri::build_miri_sysroot(self, compiler, target); + cargo.env("MIRI_SYSROOT", &miri_sysroot); + cargo.env("MIRI_HOST_SYSROOT", &host_sysroot); + } + cargo.env(profile_var("STRIP"), self.config.rust_strip.to_string()); if let Some(stack_protector) = &self.config.rust_stack_protector { @@ -2067,13 +2093,20 @@ impl<'a> Builder<'a> { rustflags.arg("-Zinline-mir"); } - // set rustc args passed from command line - let rustc_args = - self.config.cmd.rustc_args().iter().map(|s| s.to_string()).collect::>(); - if !rustc_args.is_empty() { - cargo.env("RUSTFLAGS", &rustc_args.join(" ")); + if self.config.rustc_parallel + && matches!(mode, Mode::ToolRustc | Mode::Rustc | Mode::Codegen) + { + // keep in sync with `bootstrap/lib.rs:Build::rustc_features` + // `cfg` option for rustc, `features` option for cargo, for conditional compilation + rustflags.arg("--cfg=parallel_compiler"); + rustdocflags.arg("--cfg=parallel_compiler"); } + // Pass the value of `--rustc-args` from test command. If it's not a test command, this won't set anything. + self.config.cmd.rustc_args().iter().for_each(|v| { + rustflags.arg(v); + }); + Cargo { command: cargo, compiler, @@ -2151,6 +2184,37 @@ impl<'a> Builder<'a> { out } + /// Return paths of all submodules managed by git. + /// If the current checkout is not managed by git, returns an empty slice. + pub fn get_all_submodules(&self) -> &[String] { + if !self.rust_info().is_managed_git_subrepository() { + return &[]; + } + + static SUBMODULES_PATHS: OnceLock> = OnceLock::new(); + + let init_submodules_paths = |src: &PathBuf| { + let file = File::open(src.join(".gitmodules")).unwrap(); + + let mut submodules_paths = vec![]; + for line in BufReader::new(file).lines() { + if let Ok(line) = line { + let line = line.trim(); + + if line.starts_with("path") { + let actual_path = + line.split(' ').last().expect("Couldn't get value of path"); + submodules_paths.push(actual_path.to_owned()); + } + } + } + + submodules_paths + }; + + &SUBMODULES_PATHS.get_or_init(|| init_submodules_paths(&self.src)) + } + /// Ensure that a given step is built *only if it's supposed to be built by default*, returning /// its output. This will cache the step, so it's safe (and good!) to call this as often as /// needed to ensure that all dependencies are build. @@ -2300,7 +2364,7 @@ impl Cargo { mode: Mode, source_type: SourceType, target: TargetSelection, - cmd: &str, + cmd: &str, // FIXME make this properly typed ) -> Cargo { let mut cargo = builder.cargo(compiler, mode, source_type, target, cmd); cargo.configure_linker(builder); @@ -2314,7 +2378,7 @@ impl Cargo { mode: Mode, source_type: SourceType, target: TargetSelection, - cmd: &str, + cmd: &str, // FIXME make this properly typed ) -> Cargo { builder.cargo(compiler, mode, source_type, target, cmd) } diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 0f97764055966..178df633cec68 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -599,7 +599,6 @@ mod dist { pass: None, run: None, only_modified: false, - skip: vec![], extra_checks: None, }; @@ -664,7 +663,6 @@ mod dist { no_fail_fast: false, doc: true, no_doc: false, - skip: vec![], bless: false, force_rerun: false, compare_mode: None, diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 67cde01ccdb63..96dec97525048 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -2022,7 +2022,7 @@ impl Config { Subcommand::Build { .. } => { flags.stage.or(build_stage).unwrap_or(if download_rustc { 2 } else { 1 }) } - Subcommand::Test { .. } => { + Subcommand::Test { .. } | Subcommand::Miri { .. } => { flags.stage.or(test_stage).unwrap_or(if download_rustc { 2 } else { 1 }) } Subcommand::Bench { .. } => flags.stage.or(bench_stage).unwrap_or(2), @@ -2044,6 +2044,7 @@ impl Config { if flags.stage.is_none() && crate::CiEnv::current() != crate::CiEnv::None { match config.cmd { Subcommand::Test { .. } + | Subcommand::Miri { .. } | Subcommand::Doc { .. } | Subcommand::Build { .. } | Subcommand::Bench { .. } @@ -2099,7 +2100,9 @@ impl Config { pub(crate) fn test_args(&self) -> Vec<&str> { let mut test_args = match self.cmd { - Subcommand::Test { ref test_args, .. } | Subcommand::Bench { ref test_args, .. } => { + Subcommand::Test { ref test_args, .. } + | Subcommand::Bench { ref test_args, .. } + | Subcommand::Miri { ref test_args, .. } => { test_args.iter().flat_map(|s| s.split_whitespace()).collect() } _ => vec![], diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index 7262b785ee00a..aaa2a2c47e079 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -339,9 +339,6 @@ pub enum Subcommand { #[arg(long)] /// run all tests regardless of failure no_fail_fast: bool, - #[arg(long, value_name = "SUBSTRING")] - /// skips tests matching SUBSTRING, if supported by test tool. May be passed multiple times - skip: Vec, #[arg(long, value_name = "ARGS", allow_hyphen_values(true))] /// extra arguments to be passed for the test tool being used /// (e.g. libtest, compiletest or rustdoc) @@ -382,6 +379,25 @@ pub enum Subcommand { /// `//rustfix_missing_coverage.txt` rustfix_coverage: bool, }, + /// Build and run some test suites *in Miri* + Miri { + #[arg(long)] + /// run all tests regardless of failure + no_fail_fast: bool, + #[arg(long, value_name = "ARGS", allow_hyphen_values(true))] + /// extra arguments to be passed for the test tool being used + /// (e.g. libtest, compiletest or rustdoc) + test_args: Vec, + /// extra options to pass the compiler when running tests + #[arg(long, value_name = "ARGS", allow_hyphen_values(true))] + rustc_args: Vec, + #[arg(long)] + /// do not run doc tests + no_doc: bool, + #[arg(long)] + /// only run doc tests + doc: bool, + }, /// Build and run some benchmarks Bench { #[arg(long, allow_hyphen_values(true))] @@ -453,6 +469,7 @@ impl Subcommand { Subcommand::Fix { .. } => Kind::Fix, Subcommand::Format { .. } => Kind::Format, Subcommand::Test { .. } => Kind::Test, + Subcommand::Miri { .. } => Kind::Miri, Subcommand::Clean { .. } => Kind::Clean, Subcommand::Dist { .. } => Kind::Dist, Subcommand::Install { .. } => Kind::Install, @@ -464,7 +481,7 @@ impl Subcommand { pub fn rustc_args(&self) -> Vec<&str> { match *self { - Subcommand::Test { ref rustc_args, .. } => { + Subcommand::Test { ref rustc_args, .. } | Subcommand::Miri { ref rustc_args, .. } => { rustc_args.iter().flat_map(|s| s.split_whitespace()).collect() } _ => vec![], @@ -473,14 +490,16 @@ impl Subcommand { pub fn fail_fast(&self) -> bool { match *self { - Subcommand::Test { no_fail_fast, .. } => !no_fail_fast, + Subcommand::Test { no_fail_fast, .. } | Subcommand::Miri { no_fail_fast, .. } => { + !no_fail_fast + } _ => false, } } pub fn doc_tests(&self) -> DocTests { match *self { - Subcommand::Test { doc, no_doc, .. } => { + Subcommand::Test { doc, no_doc, .. } | Subcommand::Miri { no_doc, doc, .. } => { if doc { DocTests::Only } else if no_doc { diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index d8397ab51de3c..44452446eb87f 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -250,6 +250,8 @@ pub enum Mode { /// directory. This is for miscellaneous sets of tools that are built /// using the bootstrap stage0 compiler in its entirety (target libraries /// and all). Typically these tools compile with stable Rust. + /// + /// Only works for stage 0. ToolBootstrap, /// Build a tool which uses the locally built std, placing output in the diff --git a/src/bootstrap/src/utils/render_tests.rs b/src/bootstrap/src/utils/render_tests.rs index 70f25b2cc87c1..95cd55c9a3d7c 100644 --- a/src/bootstrap/src/utils/render_tests.rs +++ b/src/bootstrap/src/utils/render_tests.rs @@ -231,14 +231,16 @@ impl<'a> Renderer<'a> { print!("\ntest result: "); self.builder.colored_stdout(|stdout| outcome.write_long(stdout)).unwrap(); println!( - ". {} passed; {} failed; {} ignored; {} measured; {} filtered out; \ - finished in {:.2?}\n", + ". {} passed; {} failed; {} ignored; {} measured; {} filtered out{time}\n", suite.passed, suite.failed, suite.ignored, suite.measured, suite.filtered_out, - Duration::from_secs_f64(suite.exec_time) + time = match suite.exec_time { + Some(t) => format!("; finished in {:.2?}", Duration::from_secs_f64(t)), + None => format!(""), + } ); } @@ -374,7 +376,9 @@ struct SuiteOutcome { ignored: usize, measured: usize, filtered_out: usize, - exec_time: f64, + /// The time it took to execute this test suite, or `None` if time measurement was not possible + /// (e.g. due to running inside Miri). + exec_time: Option, } #[derive(serde_derive::Deserialize)] diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile index e5aa81d83d5a1..6918574814fff 100644 --- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile @@ -60,7 +60,7 @@ ENV SCRIPT python3 ../x.py --stage 2 test src/tools/expand-yaml-anchors && \ /scripts/validate-error-codes.sh && \ reuse --include-submodules lint && \ # Runs checks to ensure that there are no ES5 issues in our JS code. - es-check es6 ../src/librustdoc/html/static/js/*.js && \ + es-check es8 ../src/librustdoc/html/static/js/*.js && \ eslint -c ../src/librustdoc/html/static/.eslintrc.js ../src/librustdoc/html/static/js/*.js && \ eslint -c ../src/tools/rustdoc-js/.eslintrc.js ../src/tools/rustdoc-js/tester.js && \ eslint -c ../src/tools/rustdoc-gui/.eslintrc.js ../src/tools/rustdoc-gui/tester.js diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version index a2b63962ba11e..14a8c24575690 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version @@ -1 +1 @@ -0.16.11 \ No newline at end of file +0.17.1 \ No newline at end of file diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh index f5c426a717f78..38c5b173ae31b 100755 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh @@ -2,6 +2,7 @@ # ignore-tidy-linelength set -eu +set -x # so one can see where we are in the script X_PY="$1" @@ -61,3 +62,8 @@ case $HOST_TARGET in exit 1 ;; esac +# Also smoke-test `x.py miri`. This doesn't run any actual tests (that would take too long), +# but it ensures that the crates build properly when tested with Miri. +python3 "$X_PY" miri --stage 2 library/core --test-args notest +python3 "$X_PY" miri --stage 2 library/alloc --test-args notest +python3 "$X_PY" miri --stage 2 library/std --test-args notest diff --git a/src/ci/docker/run.sh b/src/ci/docker/run.sh index 740eb7504f87b..9d72fd8a55a7f 100755 --- a/src/ci/docker/run.sh +++ b/src/ci/docker/run.sh @@ -323,6 +323,7 @@ docker \ --env GITHUB_ACTIONS \ --env GITHUB_REF \ --env GITHUB_STEP_SUMMARY="/checkout/obj/${SUMMARY_FILE}" \ + --env RUST_BACKTRACE \ --env TOOLSTATE_REPO_ACCESS_TOKEN \ --env TOOLSTATE_REPO \ --env TOOLSTATE_PUBLISH \ diff --git a/src/ci/scripts/upload-artifacts.sh b/src/ci/scripts/upload-artifacts.sh index 9755edb6dce58..c9c85ec20b45a 100755 --- a/src/ci/scripts/upload-artifacts.sh +++ b/src/ci/scripts/upload-artifacts.sh @@ -45,3 +45,17 @@ deploy_url="s3://${DEPLOY_BUCKET}/${deploy_dir}/$(ciCommit)" retry aws s3 cp --storage-class INTELLIGENT_TIERING \ --no-progress --recursive --acl public-read "${upload_dir}" "${deploy_url}" + +access_url="https://ci-artifacts.rust-lang.org/${deploy_dir}/$(ciCommit)" + +# Output URLs to the uploaded artifacts to GitHub summary (if it is available) +# to make them easily accessible. +if [ -n "${GITHUB_STEP_SUMMARY}" ] +then + echo "# CI artifacts" >> "${GITHUB_STEP_SUMMARY}" + + for filename in "${upload_dir}"/*.xz; do + filename=`basename "${filename}"` + echo "- [${filename}](${access_url}/${filename})" >> "${GITHUB_STEP_SUMMARY}" + done +fi diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md index 4a4f1ae98e407..c8f5d64957030 100644 --- a/src/doc/rustc/src/codegen-options/index.md +++ b/src/doc/rustc/src/codegen-options/index.md @@ -553,9 +553,17 @@ Supported values for this option are: of MSVC). - `debuginfo` - debuginfo sections and debuginfo symbols from the symbol table section are stripped at link time and are not copied to the produced binary - or separate files. -- `symbols` - same as `debuginfo`, but the rest of the symbol table section is - stripped as well if the linker supports it. + or separate files. This should leave backtraces mostly-intact but may make + using a debugger like gdb or lldb ineffectual. +- `symbols` - same as `debuginfo`, but the rest of the symbol table section is stripped as well, + depending on platform support. On platforms which depend on this symbol table for backtraces, + profiling, and similar, this can affect them so negatively as to make the trace incomprehensible. + Programs which may be combined with others, such as CLI pipelines and developer tooling, + or even anything which wants crash-reporting, should usually avoid `-Cstrip=symbols`. + +Note that, at any level, removing debuginfo only necessarily impacts "friendly" introspection. +`-Cstrip` cannot be relied on as a meaningful security or obfuscation measure, as disassemblers +and decompilers can extract considerable information even in the absence of symbols. ## symbol-mangling-version diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 75d38dd20bdeb..aa982a445033d 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -203,6 +203,7 @@ target | std | notes [`x86_64-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 64-bit UEFI [^x86_32-floats-x87]: Floating-point support on `i586` targets is non-compliant: the `x87` registers and instructions used for these targets do not provide IEEE-754-compliant behavior, in particular when it comes to rounding and NaN payload bits. See [issue #114479][x86-32-float-issue]. + [wasi-rename]: https://github.com/rust-lang/compiler-team/issues/607 [Fortanix ABI]: https://edp.fortanix.com/ diff --git a/src/doc/rustc/src/platform-support/netbsd.md b/src/doc/rustc/src/platform-support/netbsd.md index 3891d6d3148da..ef9337befa643 100644 --- a/src/doc/rustc/src/platform-support/netbsd.md +++ b/src/doc/rustc/src/platform-support/netbsd.md @@ -13,7 +13,7 @@ are currently defined running NetBSD: | Target name | NetBSD Platform | |--------------------------------|-----------------| -| `amd64-unknown-netbsd` | [amd64 / x86_64 systems](https://wiki.netbsd.org/ports/amd64/) | +| `x86_64-unknown-netbsd` | [amd64 / x86_64 systems](https://wiki.netbsd.org/ports/amd64/) | | `armv7-unknown-netbsd-eabihf` | [32-bit ARMv7 systems with hard-float](https://wiki.netbsd.org/ports/evbarm/) | | `armv6-unknown-netbsd-eabihf` | [32-bit ARMv6 systems with hard-float](https://wiki.netbsd.org/ports/evbarm/) | | `aarch64-unknown-netbsd` | [64-bit ARM systems, little-endian](https://wiki.netbsd.org/ports/evbarm/) | @@ -22,7 +22,7 @@ are currently defined running NetBSD: | `i686-unknown-netbsd` | [32-bit i386 with SSE](https://wiki.netbsd.org/ports/i386/) | | `mipsel-unknown-netbsd` | [32-bit mips, requires mips32 cpu support](https://wiki.netbsd.org/ports/evbmips/) | | `powerpc-unknown-netbsd` | [Various 32-bit PowerPC systems, e.g. MacPPC](https://wiki.netbsd.org/ports/macppc/) | -| `riscv64gc-unknown-netbsd` | [64-bit RISC-V](https://wiki.netbsd.org/ports/riscv/) +| `riscv64gc-unknown-netbsd` | [64-bit RISC-V](https://wiki.netbsd.org/ports/riscv/) | | `sparc64-unknown-netbsd` | [Sun UltraSPARC systems](https://wiki.netbsd.org/ports/sparc64/) | All use the "native" `stdc++` library which goes along with the natively @@ -43,7 +43,7 @@ bug reporting system. ## Requirements -The `amd64-unknown-netbsd` artifacts is being distributed by the +The `x86_64-unknown-netbsd` artifacts is being distributed by the rust project. The other targets are built by the designated developers (see above), @@ -95,7 +95,7 @@ capable systems we build and test `firefox` (amd64, i386, aarch64). ## Building Rust programs -Rust ships pre-compiled artifacts for the `amd64-unknown-netbsd` +Rust ships pre-compiled artifacts for the `x86_64-unknown-netbsd` target. For the other systems mentioned above, using the `pkgsrc` route is diff --git a/src/doc/rustdoc/src/what-is-rustdoc.md b/src/doc/rustdoc/src/what-is-rustdoc.md index 7179ee0cf0370..cb4ec51caf208 100644 --- a/src/doc/rustdoc/src/what-is-rustdoc.md +++ b/src/doc/rustdoc/src/what-is-rustdoc.md @@ -34,6 +34,9 @@ the main page is located in `doc/lib/index.html`. If you open that up in a web browser, you will see a page with a search bar, and "Crate lib" at the top, with no contents. +You can also use `cargo doc` to generate documentation for the whole project. +See [Using rustdoc with Cargo](#using-rustdoc-with-cargo). + ## Configuring rustdoc There are two problems with this: first, why does it @@ -79,7 +82,13 @@ docs. Instead of the `rustdoc` command, we could have done this: $ cargo doc ``` -Internally, this calls out to `rustdoc` like this: +If you want `cargo` to automatically open the generated documentation, you can use: + +```bash +$ cargo doc --open +``` + +Internally, `cargo doc` calls out to `rustdoc` like this: ```bash $ rustdoc --crate-name docs src/lib.rs -o /docs/target/doc -L diff --git a/src/doc/unstable-book/src/compiler-flags/external-clangrt.md b/src/doc/unstable-book/src/compiler-flags/external-clangrt.md new file mode 100644 index 0000000000000..76b78d733e515 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/external-clangrt.md @@ -0,0 +1,6 @@ +# `external-clangrt` + +This option controls whether the compiler links in its own runtime library for +[sanitizers](./sanitizer.md). Passing this flag makes the compiler *not* link +its own library. For more information, see the section in the sanitizers doc on +[working with other languages.](./sanitizer.md#working-with-other-languages) diff --git a/src/doc/unstable-book/src/compiler-flags/remap-path-scope.md b/src/doc/unstable-book/src/compiler-flags/remap-path-scope.md index 424f1128e3b5c..65219dc68e976 100644 --- a/src/doc/unstable-book/src/compiler-flags/remap-path-scope.md +++ b/src/doc/unstable-book/src/compiler-flags/remap-path-scope.md @@ -10,11 +10,9 @@ This flag accepts a comma-separated list of values and may be specified multiple - `macro` - apply remappings to the expansion of `std::file!()` macro. This is where paths in embedded panic messages come from - `diagnostics` - apply remappings to printed compiler diagnostics -- `unsplit-debuginfo` - apply remappings to debug information only when they are written to compiled executables or libraries, but not when they are in split debuginfo files -- `split-debuginfo` - apply remappings to debug information only when they are written to split debug information files, but not in compiled executables or libraries -- `split-debuginfo-path` - apply remappings to the paths pointing to split debug information files. Does nothing when these files are not generated. -- `object` - an alias for `macro,unsplit-debuginfo,split-debuginfo-path`. This ensures all paths in compiled executables or libraries are remapped, but not elsewhere. -- `all` and `true` - an alias for all of the above, also equivalent to supplying only `--remap-path-prefix` without `--remap-path-scope`. +- `debuginfo` - apply remappings to debug informations +- `object` - apply remappings to all paths in compiled executables or libraries, but not elsewhere. Currently an alias for `macro,debuginfo`. +- `all` - an alias for all of the above, also equivalent to supplying only `--remap-path-prefix` without `--remap-path-scope`. ## Example ```sh diff --git a/src/doc/unstable-book/src/compiler-flags/sanitizer.md b/src/doc/unstable-book/src/compiler-flags/sanitizer.md index c8fd154a00ec9..72b44e002b4d4 100644 --- a/src/doc/unstable-book/src/compiler-flags/sanitizer.md +++ b/src/doc/unstable-book/src/compiler-flags/sanitizer.md @@ -45,6 +45,9 @@ To enable a sanitizer compile with `-Zsanitizer=address`, `-Zsanitizer=cfi`, `-Zsanitizer=dataflow`,`-Zsanitizer=hwaddress`, `-Zsanitizer=leak`, `-Zsanitizer=memory`, `-Zsanitizer=memtag`, `-Zsanitizer=shadow-call-stack`, or `-Zsanitizer=thread`. You might also need the `--target` and `build-std` flags. +If you're working with other languages that are also instrumented with sanitizers, +you might need the `external-clangrt` flag. See the section on +[working with other languages](#working-with-other-languages). Example: ```shell @@ -853,6 +856,18 @@ functionality][build-std]. [build-std]: ../../cargo/reference/unstable.html#build-std +# Working with other languages + +Sanitizers rely on compiler runtime libraries to function properly. Rust links +in its own compiler runtime which might conflict with runtimes required by +languages such as C++. Since Rust's runtime doesn't always contain the symbols +required by C++ instrumented code, you might need to skip linking it so another +runtime can be linked instead. + +A separate unstable option `-Zexternal-clangrt` can be used to make rustc skip +linking the compiler runtime for the sanitizer. This will require you to link +in an external runtime, such as from clang instead. + # Build scripts and procedural macros Use of sanitizers together with build scripts and procedural macros is diff --git a/src/etc/completions/x.py.fish b/src/etc/completions/x.py.fish index 9fc95fd09ba9f..5ae27bc8c91c5 100644 --- a/src/etc/completions/x.py.fish +++ b/src/etc/completions/x.py.fish @@ -39,6 +39,7 @@ complete -c x.py -n "__fish_use_subcommand" -f -a "fix" -d 'Run cargo fix' complete -c x.py -n "__fish_use_subcommand" -f -a "fmt" -d 'Run rustfmt' complete -c x.py -n "__fish_use_subcommand" -f -a "doc" -d 'Build documentation' complete -c x.py -n "__fish_use_subcommand" -f -a "test" -d 'Build and run some test suites' +complete -c x.py -n "__fish_use_subcommand" -f -a "miri" -d 'Build and run some test suites *in Miri*' complete -c x.py -n "__fish_use_subcommand" -f -a "bench" -d 'Build and run some benchmarks' complete -c x.py -n "__fish_use_subcommand" -f -a "clean" -d 'Clean out build directories' complete -c x.py -n "__fish_use_subcommand" -f -a "dist" -d 'Build distribution artifacts' @@ -261,7 +262,6 @@ complete -c x.py -n "__fish_seen_subcommand_from doc" -l llvm-profile-generate - complete -c x.py -n "__fish_seen_subcommand_from doc" -l enable-bolt-settings -d 'Enable BOLT link flags' complete -c x.py -n "__fish_seen_subcommand_from doc" -l skip-stage0-validation -d 'Skip stage0 compiler validation' complete -c x.py -n "__fish_seen_subcommand_from doc" -s h -l help -d 'Print help (see more with \'--help\')' -complete -c x.py -n "__fish_seen_subcommand_from test" -l skip -d 'skips tests matching SUBSTRING, if supported by test tool. May be passed multiple times' -r -F complete -c x.py -n "__fish_seen_subcommand_from test" -l test-args -d 'extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)' -r complete -c x.py -n "__fish_seen_subcommand_from test" -l rustc-args -d 'extra options to pass the compiler when running tests' -r complete -c x.py -n "__fish_seen_subcommand_from test" -l extra-checks -d 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell)' -r @@ -274,6 +274,7 @@ complete -c x.py -n "__fish_seen_subcommand_from test" -l build -d 'build target complete -c x.py -n "__fish_seen_subcommand_from test" -l host -d 'host targets to build' -r -f complete -c x.py -n "__fish_seen_subcommand_from test" -l target -d 'target targets to build' -r -f complete -c x.py -n "__fish_seen_subcommand_from test" -l exclude -d 'build paths to exclude' -r -F +complete -c x.py -n "__fish_seen_subcommand_from test" -l skip -d 'build paths to skip' -r -F complete -c x.py -n "__fish_seen_subcommand_from test" -l rustc-error-format -r -f complete -c x.py -n "__fish_seen_subcommand_from test" -l on-fail -d 'command to run on failure' -r -f -a "(__fish_complete_command)" complete -c x.py -n "__fish_seen_subcommand_from test" -l stage -d 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)' -r -f @@ -308,6 +309,45 @@ complete -c x.py -n "__fish_seen_subcommand_from test" -l llvm-profile-generate complete -c x.py -n "__fish_seen_subcommand_from test" -l enable-bolt-settings -d 'Enable BOLT link flags' complete -c x.py -n "__fish_seen_subcommand_from test" -l skip-stage0-validation -d 'Skip stage0 compiler validation' complete -c x.py -n "__fish_seen_subcommand_from test" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c x.py -n "__fish_seen_subcommand_from miri" -l test-args -d 'extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)' -r +complete -c x.py -n "__fish_seen_subcommand_from miri" -l rustc-args -d 'extra options to pass the compiler when running tests' -r +complete -c x.py -n "__fish_seen_subcommand_from miri" -l config -d 'TOML configuration file for build' -r -F +complete -c x.py -n "__fish_seen_subcommand_from miri" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)" +complete -c x.py -n "__fish_seen_subcommand_from miri" -l build -d 'build target of the stage0 compiler' -r -f +complete -c x.py -n "__fish_seen_subcommand_from miri" -l host -d 'host targets to build' -r -f +complete -c x.py -n "__fish_seen_subcommand_from miri" -l target -d 'target targets to build' -r -f +complete -c x.py -n "__fish_seen_subcommand_from miri" -l exclude -d 'build paths to exclude' -r -F +complete -c x.py -n "__fish_seen_subcommand_from miri" -l skip -d 'build paths to skip' -r -F +complete -c x.py -n "__fish_seen_subcommand_from miri" -l rustc-error-format -r -f +complete -c x.py -n "__fish_seen_subcommand_from miri" -l on-fail -d 'command to run on failure' -r -f -a "(__fish_complete_command)" +complete -c x.py -n "__fish_seen_subcommand_from miri" -l stage -d 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)' -r -f +complete -c x.py -n "__fish_seen_subcommand_from miri" -l keep-stage -d 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x.py -n "__fish_seen_subcommand_from miri" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x.py -n "__fish_seen_subcommand_from miri" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)" +complete -c x.py -n "__fish_seen_subcommand_from miri" -s j -l jobs -d 'number of jobs to run in parallel' -r -f +complete -c x.py -n "__fish_seen_subcommand_from miri" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny '',warn '',default ''}" +complete -c x.py -n "__fish_seen_subcommand_from miri" -l error-format -d 'rustc error format' -r -f +complete -c x.py -n "__fish_seen_subcommand_from miri" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always '',never '',auto ''}" +complete -c x.py -n "__fish_seen_subcommand_from miri" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true '',false ''}" +complete -c x.py -n "__fish_seen_subcommand_from miri" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F +complete -c x.py -n "__fish_seen_subcommand_from miri" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F +complete -c x.py -n "__fish_seen_subcommand_from miri" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F +complete -c x.py -n "__fish_seen_subcommand_from miri" -l reproducible-artifact -d 'Additional reproducible artifacts that should be added to the reproducible artifacts archive' -r +complete -c x.py -n "__fish_seen_subcommand_from miri" -l set -d 'override options in config.toml' -r -f +complete -c x.py -n "__fish_seen_subcommand_from miri" -l no-fail-fast -d 'run all tests regardless of failure' +complete -c x.py -n "__fish_seen_subcommand_from miri" -l no-doc -d 'do not run doc tests' +complete -c x.py -n "__fish_seen_subcommand_from miri" -l doc -d 'only run doc tests' +complete -c x.py -n "__fish_seen_subcommand_from miri" -s v -l verbose -d 'use verbose output (-vv for very verbose)' +complete -c x.py -n "__fish_seen_subcommand_from miri" -s i -l incremental -d 'use incremental compilation' +complete -c x.py -n "__fish_seen_subcommand_from miri" -l include-default-paths -d 'include default paths in addition to the provided ones' +complete -c x.py -n "__fish_seen_subcommand_from miri" -l dry-run -d 'dry run; don\'t build anything' +complete -c x.py -n "__fish_seen_subcommand_from miri" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims' +complete -c x.py -n "__fish_seen_subcommand_from miri" -l json-output -d 'use message-format=json' +complete -c x.py -n "__fish_seen_subcommand_from miri" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)' +complete -c x.py -n "__fish_seen_subcommand_from miri" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc' +complete -c x.py -n "__fish_seen_subcommand_from miri" -l enable-bolt-settings -d 'Enable BOLT link flags' +complete -c x.py -n "__fish_seen_subcommand_from miri" -l skip-stage0-validation -d 'Skip stage0 compiler validation' +complete -c x.py -n "__fish_seen_subcommand_from miri" -s h -l help -d 'Print help (see more with \'--help\')' complete -c x.py -n "__fish_seen_subcommand_from bench" -l test-args -r complete -c x.py -n "__fish_seen_subcommand_from bench" -l config -d 'TOML configuration file for build' -r -F complete -c x.py -n "__fish_seen_subcommand_from bench" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)" diff --git a/src/etc/completions/x.py.ps1 b/src/etc/completions/x.py.ps1 index 6359b7ff086a9..d39639a1f91d8 100644 --- a/src/etc/completions/x.py.ps1 +++ b/src/etc/completions/x.py.ps1 @@ -66,6 +66,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('fmt', 'fmt', [CompletionResultType]::ParameterValue, 'Run rustfmt') [CompletionResult]::new('doc', 'doc', [CompletionResultType]::ParameterValue, 'Build documentation') [CompletionResult]::new('test', 'test', [CompletionResultType]::ParameterValue, 'Build and run some test suites') + [CompletionResult]::new('miri', 'miri', [CompletionResultType]::ParameterValue, 'Build and run some test suites *in Miri*') [CompletionResult]::new('bench', 'bench', [CompletionResultType]::ParameterValue, 'Build and run some benchmarks') [CompletionResult]::new('clean', 'clean', [CompletionResultType]::ParameterValue, 'Clean out build directories') [CompletionResult]::new('dist', 'dist', [CompletionResultType]::ParameterValue, 'Build distribution artifacts') @@ -333,7 +334,6 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { break } 'x.py;test' { - [CompletionResult]::new('--skip', 'skip', [CompletionResultType]::ParameterName, 'skips tests matching SUBSTRING, if supported by test tool. May be passed multiple times') [CompletionResult]::new('--test-args', 'test-args', [CompletionResultType]::ParameterName, 'extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)') [CompletionResult]::new('--rustc-args', 'rustc-args', [CompletionResultType]::ParameterName, 'extra options to pass the compiler when running tests') [CompletionResult]::new('--extra-checks', 'extra-checks', [CompletionResultType]::ParameterName, 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell)') @@ -346,6 +346,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--host', 'host', [CompletionResultType]::ParameterName, 'host targets to build') [CompletionResult]::new('--target', 'target', [CompletionResultType]::ParameterName, 'target targets to build') [CompletionResult]::new('--exclude', 'exclude', [CompletionResultType]::ParameterName, 'build paths to exclude') + [CompletionResult]::new('--skip', 'skip', [CompletionResultType]::ParameterName, 'build paths to skip') [CompletionResult]::new('--rustc-error-format', 'rustc-error-format', [CompletionResultType]::ParameterName, 'rustc-error-format') [CompletionResult]::new('--on-fail', 'on-fail', [CompletionResultType]::ParameterName, 'command to run on failure') [CompletionResult]::new('--stage', 'stage', [CompletionResultType]::ParameterName, 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)') @@ -386,6 +387,52 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') break } + 'x.py;miri' { + [CompletionResult]::new('--test-args', 'test-args', [CompletionResultType]::ParameterName, 'extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)') + [CompletionResult]::new('--rustc-args', 'rustc-args', [CompletionResultType]::ParameterName, 'extra options to pass the compiler when running tests') + [CompletionResult]::new('--config', 'config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') + [CompletionResult]::new('--build-dir', 'build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `config.toml`') + [CompletionResult]::new('--build', 'build', [CompletionResultType]::ParameterName, 'build target of the stage0 compiler') + [CompletionResult]::new('--host', 'host', [CompletionResultType]::ParameterName, 'host targets to build') + [CompletionResult]::new('--target', 'target', [CompletionResultType]::ParameterName, 'target targets to build') + [CompletionResult]::new('--exclude', 'exclude', [CompletionResultType]::ParameterName, 'build paths to exclude') + [CompletionResult]::new('--skip', 'skip', [CompletionResultType]::ParameterName, 'build paths to skip') + [CompletionResult]::new('--rustc-error-format', 'rustc-error-format', [CompletionResultType]::ParameterName, 'rustc-error-format') + [CompletionResult]::new('--on-fail', 'on-fail', [CompletionResultType]::ParameterName, 'command to run on failure') + [CompletionResult]::new('--stage', 'stage', [CompletionResultType]::ParameterName, 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)') + [CompletionResult]::new('--keep-stage', 'keep-stage', [CompletionResultType]::ParameterName, 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)') + [CompletionResult]::new('--keep-stage-std', 'keep-stage-std', [CompletionResultType]::ParameterName, 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)') + [CompletionResult]::new('--src', 'src', [CompletionResultType]::ParameterName, 'path to the root of the rust checkout') + [CompletionResult]::new('-j', 'j', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel') + [CompletionResult]::new('--jobs', 'jobs', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel') + [CompletionResult]::new('--warnings', 'warnings', [CompletionResultType]::ParameterName, 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour') + [CompletionResult]::new('--error-format', 'error-format', [CompletionResultType]::ParameterName, 'rustc error format') + [CompletionResult]::new('--color', 'color', [CompletionResultType]::ParameterName, 'whether to use color in cargo and rustc output') + [CompletionResult]::new('--llvm-skip-rebuild', 'llvm-skip-rebuild', [CompletionResultType]::ParameterName, 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml') + [CompletionResult]::new('--rust-profile-generate', 'rust-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with rustc build') + [CompletionResult]::new('--rust-profile-use', 'rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build') + [CompletionResult]::new('--llvm-profile-use', 'llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build') + [CompletionResult]::new('--reproducible-artifact', 'reproducible-artifact', [CompletionResultType]::ParameterName, 'Additional reproducible artifacts that should be added to the reproducible artifacts archive') + [CompletionResult]::new('--set', 'set', [CompletionResultType]::ParameterName, 'override options in config.toml') + [CompletionResult]::new('--no-fail-fast', 'no-fail-fast', [CompletionResultType]::ParameterName, 'run all tests regardless of failure') + [CompletionResult]::new('--no-doc', 'no-doc', [CompletionResultType]::ParameterName, 'do not run doc tests') + [CompletionResult]::new('--doc', 'doc', [CompletionResultType]::ParameterName, 'only run doc tests') + [CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') + [CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') + [CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'use incremental compilation') + [CompletionResult]::new('--incremental', 'incremental', [CompletionResultType]::ParameterName, 'use incremental compilation') + [CompletionResult]::new('--include-default-paths', 'include-default-paths', [CompletionResultType]::ParameterName, 'include default paths in addition to the provided ones') + [CompletionResult]::new('--dry-run', 'dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything') + [CompletionResult]::new('--dump-bootstrap-shims', 'dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims') + [CompletionResult]::new('--json-output', 'json-output', [CompletionResultType]::ParameterName, 'use message-format=json') + [CompletionResult]::new('--bypass-bootstrap-lock', 'bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)') + [CompletionResult]::new('--llvm-profile-generate', 'llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc') + [CompletionResult]::new('--enable-bolt-settings', 'enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags') + [CompletionResult]::new('--skip-stage0-validation', 'skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation') + [CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') + [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') + break + } 'x.py;bench' { [CompletionResult]::new('--test-args', 'test-args', [CompletionResultType]::ParameterName, 'test-args') [CompletionResult]::new('--config', 'config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') diff --git a/src/etc/completions/x.py.sh b/src/etc/completions/x.py.sh index e1436dcde6704..1f1a9a70767fa 100644 --- a/src/etc/completions/x.py.sh +++ b/src/etc/completions/x.py.sh @@ -42,6 +42,9 @@ _x.py() { bootstrap,install) cmd="bootstrap__install" ;; + bootstrap,miri) + cmd="bootstrap__miri" + ;; bootstrap,run) cmd="bootstrap__run" ;; @@ -61,7 +64,7 @@ _x.py() { case "${cmd}" in x.py) - opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]... build check clippy fix fmt doc test bench clean dist install run setup suggest" + opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]... build check clippy fix fmt doc test miri bench clean dist install run setup suggest" if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -1290,6 +1293,124 @@ _x.py() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; + x.py__miri) + opts="-v -i -j -h --no-fail-fast --test-args --rustc-args --no-doc --doc --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..." + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --test-args) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --rustc-args) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --config) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --build-dir) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --build) + COMPREPLY=("${cur}") + return 0 + ;; + --host) + COMPREPLY=("${cur}") + return 0 + ;; + --target) + COMPREPLY=("${cur}") + return 0 + ;; + --exclude) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --skip) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --rustc-error-format) + COMPREPLY=("${cur}") + return 0 + ;; + --on-fail) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --stage) + COMPREPLY=("${cur}") + return 0 + ;; + --keep-stage) + COMPREPLY=("${cur}") + return 0 + ;; + --keep-stage-std) + COMPREPLY=("${cur}") + return 0 + ;; + --src) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --jobs) + COMPREPLY=("${cur}") + return 0 + ;; + -j) + COMPREPLY=("${cur}") + return 0 + ;; + --warnings) + COMPREPLY=($(compgen -W "deny warn default" -- "${cur}")) + return 0 + ;; + --error-format) + COMPREPLY=("${cur}") + return 0 + ;; + --color) + COMPREPLY=($(compgen -W "always never auto" -- "${cur}")) + return 0 + ;; + --llvm-skip-rebuild) + COMPREPLY=($(compgen -W "true false" -- "${cur}")) + return 0 + ;; + --rust-profile-generate) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --rust-profile-use) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --llvm-profile-use) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --reproducible-artifact) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --set) + COMPREPLY=("${cur}") + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; x.py__run) opts="-v -i -j -h --args --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then @@ -1625,16 +1746,12 @@ _x.py() { return 0 ;; x.py__test) - opts="-v -i -j -h --no-fail-fast --skip --test-args --rustc-args --no-doc --doc --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --no-fail-fast --test-args --rustc-args --no-doc --doc --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 fi case "${prev}" in - --skip) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; --test-args) COMPREPLY=($(compgen -f "${cur}")) return 0 @@ -1683,6 +1800,10 @@ _x.py() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --skip) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; --rustc-error-format) COMPREPLY=("${cur}") return 0 diff --git a/src/etc/completions/x.py.zsh b/src/etc/completions/x.py.zsh index ea7e4ba6758f5..b920309ac2c15 100644 --- a/src/etc/completions/x.py.zsh +++ b/src/etc/completions/x.py.zsh @@ -335,7 +335,6 @@ _arguments "${_arguments_options[@]}" \ ;; (test) _arguments "${_arguments_options[@]}" \ -'*--skip=[skips tests matching SUBSTRING, if supported by test tool. May be passed multiple times]:SUBSTRING:_files' \ '*--test-args=[extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)]:ARGS: ' \ '*--rustc-args=[extra options to pass the compiler when running tests]:ARGS: ' \ '--extra-checks=[comma-separated list of other files types to check (accepts py, py\:lint, py\:fmt, shell)]:EXTRA_CHECKS: ' \ @@ -348,6 +347,7 @@ _arguments "${_arguments_options[@]}" \ '--host=[host targets to build]:HOST:( )' \ '--target=[target targets to build]:TARGET:( )' \ '*--exclude=[build paths to exclude]:PATH:_files' \ +'*--skip=[build paths to skip]:PATH:_files' \ '--rustc-error-format=[]:RUSTC_ERROR_FORMAT:( )' \ '--on-fail=[command to run on failure]:CMD:_cmdstring' \ '--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:( )' \ @@ -389,6 +389,54 @@ _arguments "${_arguments_options[@]}" \ '*::paths -- paths for the subcommand:_files' \ && ret=0 ;; +(miri) +_arguments "${_arguments_options[@]}" \ +'*--test-args=[extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)]:ARGS: ' \ +'*--rustc-args=[extra options to pass the compiler when running tests]:ARGS: ' \ +'--config=[TOML configuration file for build]:FILE:_files' \ +'--build-dir=[Build directory, overrides \`build.build-dir\` in \`config.toml\`]:DIR:_files -/' \ +'--build=[build target of the stage0 compiler]:BUILD:( )' \ +'--host=[host targets to build]:HOST:( )' \ +'--target=[target targets to build]:TARGET:( )' \ +'*--exclude=[build paths to exclude]:PATH:_files' \ +'*--skip=[build paths to skip]:PATH:_files' \ +'--rustc-error-format=[]:RUSTC_ERROR_FORMAT:( )' \ +'--on-fail=[command to run on failure]:CMD:_cmdstring' \ +'--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:( )' \ +'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \ +'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \ +'--src=[path to the root of the rust checkout]:DIR:_files -/' \ +'-j+[number of jobs to run in parallel]:JOBS:( )' \ +'--jobs=[number of jobs to run in parallel]:JOBS:( )' \ +'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \ +'--error-format=[rustc error format]:FORMAT:( )' \ +'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \ +'--llvm-skip-rebuild=[whether rebuilding llvm should be skipped, overriding \`skip-rebuld\` in config.toml]:VALUE:(true false)' \ +'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \ +'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \ +'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \ +'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT: ' \ +'*--set=[override options in config.toml]:section.option=value:( )' \ +'--no-fail-fast[run all tests regardless of failure]' \ +'--no-doc[do not run doc tests]' \ +'--doc[only run doc tests]' \ +'*-v[use verbose output (-vv for very verbose)]' \ +'*--verbose[use verbose output (-vv for very verbose)]' \ +'-i[use incremental compilation]' \ +'--incremental[use incremental compilation]' \ +'--include-default-paths[include default paths in addition to the provided ones]' \ +'--dry-run[dry run; don'\''t build anything]' \ +'--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \ +'--json-output[use message-format=json]' \ +'--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \ +'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \ +'--enable-bolt-settings[Enable BOLT link flags]' \ +'--skip-stage0-validation[Skip stage0 compiler validation]' \ +'-h[Print help (see more with '\''--help'\'')]' \ +'--help[Print help (see more with '\''--help'\'')]' \ +'*::paths -- paths for the subcommand:_files' \ +&& ret=0 +;; (bench) _arguments "${_arguments_options[@]}" \ '*--test-args=[]:TEST_ARGS: ' \ @@ -710,6 +758,7 @@ _x.py_commands() { 'fmt:Run rustfmt' \ 'doc:Build documentation' \ 'test:Build and run some test suites' \ +'miri:Build and run some test suites *in Miri*' \ 'bench:Build and run some benchmarks' \ 'clean:Clean out build directories' \ 'dist:Build distribution artifacts' \ @@ -770,6 +819,11 @@ _x.py__install_commands() { local commands; commands=() _describe -t commands 'x.py install commands' commands "$@" } +(( $+functions[_x.py__miri_commands] )) || +_x.py__miri_commands() { + local commands; commands=() + _describe -t commands 'x.py miri commands' commands "$@" +} (( $+functions[_x.py__run_commands] )) || _x.py__run_commands() { local commands; commands=() diff --git a/src/etc/lldb_commands b/src/etc/lldb_commands index 615d13ccd0ffd..4be2dba34f6f8 100644 --- a/src/etc/lldb_commands +++ b/src/etc/lldb_commands @@ -16,4 +16,7 @@ type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)R type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)RefMut<.+>$" --category Rust type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)RefCell<.+>$" --category Rust type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)NonZero<.+>$" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h "^core::num::([a-z_]+::)*NonZero.+$" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h "^(std::([a-z_]+::)+)PathBuf$" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h "^&(mut )?(std::([a-z_]+::)+)Path$" --category Rust type category enable Rust diff --git a/src/etc/lldb_lookup.py b/src/etc/lldb_lookup.py index 36c7d82b34ac7..a93b42e1cc449 100644 --- a/src/etc/lldb_lookup.py +++ b/src/etc/lldb_lookup.py @@ -58,6 +58,11 @@ def summary_lookup(valobj, dict): if rust_type == RustType.STD_NONZERO_NUMBER: return StdNonZeroNumberSummaryProvider(valobj, dict) + if rust_type == RustType.STD_PATHBUF: + return StdPathBufSummaryProvider(valobj, dict) + if rust_type == RustType.STD_PATH: + return StdPathSummaryProvider(valobj, dict) + return "" diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py index 5d2b6fd525c14..1c43977a501a0 100644 --- a/src/etc/lldb_providers.py +++ b/src/etc/lldb_providers.py @@ -173,6 +173,35 @@ def StdStrSummaryProvider(valobj, dict): return '"%s"' % data +def StdPathBufSummaryProvider(valobj, dict): + # type: (SBValue, dict) -> str + # logger = Logger.Logger() + # logger >> "[StdPathBufSummaryProvider] for " + str(valobj.GetName()) + return StdOsStringSummaryProvider(valobj.GetChildMemberWithName("inner"), dict) + + +def StdPathSummaryProvider(valobj, dict): + # type: (SBValue, dict) -> str + # logger = Logger.Logger() + # logger >> "[StdPathSummaryProvider] for " + str(valobj.GetName()) + length = valobj.GetChildMemberWithName("length").GetValueAsUnsigned() + if length == 0: + return '""' + + data_ptr = valobj.GetChildMemberWithName("data_ptr") + + start = data_ptr.GetValueAsUnsigned() + error = SBError() + process = data_ptr.GetProcess() + data = process.ReadMemory(start, length, error) + if PY3: + try: + data = data.decode(encoding='UTF-8') + except UnicodeDecodeError: + return '%r' % data + return '"%s"' % data + + class StructSyntheticProvider: """Pretty-printer for structs and struct enum variants""" diff --git a/src/etc/rust_types.py b/src/etc/rust_types.py index 2b06683ef93cb..c0415a3cdcfe4 100644 --- a/src/etc/rust_types.py +++ b/src/etc/rust_types.py @@ -32,6 +32,8 @@ class RustType(object): STD_REF_MUT = "StdRefMut" STD_REF_CELL = "StdRefCell" STD_NONZERO_NUMBER = "StdNonZeroNumber" + STD_PATH = "StdPath" + STD_PATHBUF = "StdPathBuf" STD_STRING_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)String$") @@ -51,6 +53,8 @@ class RustType(object): STD_REF_MUT_REGEX = re.compile(r"^(core::([a-z_]+::)+)RefMut<.+>$") STD_REF_CELL_REGEX = re.compile(r"^(core::([a-z_]+::)+)RefCell<.+>$") STD_NONZERO_NUMBER_REGEX = re.compile(r"^(core::([a-z_]+::)+)NonZero<.+>$") +STD_PATHBUF_REGEX = re.compile(r"^(std::([a-z_]+::)+)PathBuf$") +STD_PATH_REGEX = re.compile(r"^&(mut )?(std::([a-z_]+::)+)Path$") TUPLE_ITEM_REGEX = re.compile(r"__\d+$") @@ -75,6 +79,8 @@ class RustType(object): RustType.STD_REF_CELL: STD_REF_CELL_REGEX, RustType.STD_CELL: STD_CELL_REGEX, RustType.STD_NONZERO_NUMBER: STD_NONZERO_NUMBER_REGEX, + RustType.STD_PATHBUF: STD_PATHBUF_REGEX, + RustType.STD_PATH: STD_PATH_REGEX, } def is_tuple_fields(fields): diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index bd0fbef998b2b..9a23811ed3f97 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -9,6 +9,8 @@ path = "lib.rs" [dependencies] arrayvec = { version = "0.7", default-features = false } askama = { version = "0.12", default-features = false, features = ["config"] } +base64 = "0.21.7" +byteorder = "1.5" itertools = "0.12" indexmap = "2" minifier = "0.3.0" @@ -20,7 +22,7 @@ serde = { version = "1.0", features = ["derive"] } smallvec = "1.8.1" tempfile = "3" tracing = "0.1" -tracing-tree = "0.2.0" +tracing-tree = "0.3.0" threadpool = "1.8.1" [dependencies.tracing-subscriber] diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index fbc2c3c5af459..217f6bb550bca 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -1,747 +1,367 @@ +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry}; use rustc_hir as hir; -use rustc_hir::lang_items::LangItem; -use rustc_middle::ty::{Region, RegionVid, TypeFoldable}; -use rustc_trait_selection::traits::auto_trait::{self, AutoTraitResult}; - -use std::fmt::Debug; - -use super::*; - -#[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)] -enum RegionTarget<'tcx> { - Region(Region<'tcx>), - RegionVid(RegionVid), -} - -#[derive(Default, Debug, Clone)] -struct RegionDeps<'tcx> { - larger: FxHashSet>, - smaller: FxHashSet>, -} - -pub(crate) struct AutoTraitFinder<'a, 'tcx> { - pub(crate) cx: &'a mut core::DocContext<'tcx>, +use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData}; +use rustc_middle::bug; +use rustc_middle::ty::{self, Region, Ty}; +use rustc_span::def_id::DefId; +use rustc_span::symbol::{kw, Symbol}; +use rustc_trait_selection::traits::auto_trait::{self, RegionTarget}; + +use thin_vec::ThinVec; + +use crate::clean::{self, simplify, Lifetime}; +use crate::clean::{ + clean_generic_param_def, clean_middle_ty, clean_predicate, clean_trait_ref_with_bindings, + clean_ty_generics, +}; +use crate::core::DocContext; + +#[instrument(level = "debug", skip(cx))] +pub(crate) fn synthesize_auto_trait_impls<'tcx>( + cx: &mut DocContext<'tcx>, + item_def_id: DefId, +) -> Vec { + let tcx = cx.tcx; + let param_env = tcx.param_env(item_def_id); + let ty = tcx.type_of(item_def_id).instantiate_identity(); + + let finder = auto_trait::AutoTraitFinder::new(tcx); + let mut auto_trait_impls: Vec<_> = cx + .auto_traits + .clone() + .into_iter() + .filter_map(|trait_def_id| { + synthesize_auto_trait_impl( + cx, + ty, + trait_def_id, + param_env, + item_def_id, + &finder, + DiscardPositiveImpls::No, + ) + }) + .collect(); + // We are only interested in case the type *doesn't* implement the `Sized` trait. + if !ty.is_sized(tcx, param_env) + && let Some(sized_trait_def_id) = tcx.lang_items().sized_trait() + && let Some(impl_item) = synthesize_auto_trait_impl( + cx, + ty, + sized_trait_def_id, + param_env, + item_def_id, + &finder, + DiscardPositiveImpls::Yes, + ) + { + auto_trait_impls.push(impl_item); + } + auto_trait_impls } -impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> -where - 'tcx: 'a, // should be an implied bound; rustc bug #98852. -{ - pub(crate) fn new(cx: &'a mut core::DocContext<'tcx>) -> Self { - AutoTraitFinder { cx } +#[instrument(level = "debug", skip(cx, finder))] +fn synthesize_auto_trait_impl<'tcx>( + cx: &mut DocContext<'tcx>, + ty: Ty<'tcx>, + trait_def_id: DefId, + param_env: ty::ParamEnv<'tcx>, + item_def_id: DefId, + finder: &auto_trait::AutoTraitFinder<'tcx>, + discard_positive_impls: DiscardPositiveImpls, +) -> Option { + let tcx = cx.tcx; + let trait_ref = ty::Binder::dummy(ty::TraitRef::new(tcx, trait_def_id, [ty])); + if !cx.generated_synthetics.insert((ty, trait_def_id)) { + debug!("already generated, aborting"); + return None; } - fn generate_for_trait( - &mut self, - ty: Ty<'tcx>, - trait_def_id: DefId, - param_env: ty::ParamEnv<'tcx>, - item_def_id: DefId, - f: &auto_trait::AutoTraitFinder<'tcx>, - // If this is set, show only negative trait implementations, not positive ones. - discard_positive_impl: bool, - ) -> Option { - let tcx = self.cx.tcx; - let trait_ref = ty::Binder::dummy(ty::TraitRef::new(tcx, trait_def_id, [ty])); - if !self.cx.generated_synthetics.insert((ty, trait_def_id)) { - debug!("get_auto_trait_impl_for({trait_ref:?}): already generated, aborting"); - return None; - } + let result = finder.find_auto_trait_generics(ty, param_env, trait_def_id, |info| { + clean_param_env(cx, item_def_id, info.full_user_env, info.region_data, info.vid_to_region) + }); - let result = f.find_auto_trait_generics(ty, param_env, trait_def_id, |info| { - let region_data = info.region_data; + let (generics, polarity) = match result { + auto_trait::AutoTraitResult::PositiveImpl(generics) => { + if let DiscardPositiveImpls::Yes = discard_positive_impls { + return None; + } - let names_map = tcx - .generics_of(item_def_id) - .params - .iter() - .filter_map(|param| match param.kind { - ty::GenericParamDefKind::Lifetime => Some(param.name), - _ => None, - }) - .map(|name| (name, Lifetime(name))) - .collect(); - let lifetime_predicates = Self::handle_lifetimes(®ion_data, &names_map); - let new_generics = self.param_env_to_generics( - item_def_id, - info.full_user_env, - lifetime_predicates, - info.vid_to_region, + (generics, ty::ImplPolarity::Positive) + } + auto_trait::AutoTraitResult::NegativeImpl => { + // For negative impls, we use the generic params, but *not* the predicates, + // from the original type. Otherwise, the displayed impl appears to be a + // conditional negative impl, when it's really unconditional. + // + // For example, consider the struct Foo(*mut T). Using + // the original predicates in our impl would cause us to generate + // `impl !Send for Foo`, which makes it appear that Foo + // implements Send where T is not copy. + // + // Instead, we generate `impl !Send for Foo`, which better + // expresses the fact that `Foo` never implements `Send`, + // regardless of the choice of `T`. + let mut generics = clean_ty_generics( + cx, + tcx.generics_of(item_def_id), + ty::GenericPredicates::default(), ); + generics.where_predicates.clear(); - debug!( - "find_auto_trait_generics(item_def_id={:?}, trait_def_id={:?}): \ - finished with {:?}", - item_def_id, trait_def_id, new_generics - ); + (generics, ty::ImplPolarity::Negative) + } + auto_trait::AutoTraitResult::ExplicitImpl => return None, + }; + + Some(clean::Item { + name: None, + attrs: Default::default(), + item_id: clean::ItemId::Auto { trait_: trait_def_id, for_: item_def_id }, + kind: Box::new(clean::ImplItem(Box::new(clean::Impl { + unsafety: hir::Unsafety::Normal, + generics, + trait_: Some(clean_trait_ref_with_bindings(cx, trait_ref, ThinVec::new())), + for_: clean_middle_ty(ty::Binder::dummy(ty), cx, None, None), + items: Vec::new(), + polarity, + kind: clean::ImplKind::Auto, + }))), + cfg: None, + inline_stmt_id: None, + }) +} - new_generics - }); +#[derive(Debug)] +enum DiscardPositiveImpls { + Yes, + No, +} - let polarity; - let new_generics = match result { - AutoTraitResult::PositiveImpl(new_generics) => { - polarity = ty::ImplPolarity::Positive; - if discard_positive_impl { - return None; +#[instrument(level = "debug", skip(cx, region_data, vid_to_region))] +fn clean_param_env<'tcx>( + cx: &mut DocContext<'tcx>, + item_def_id: DefId, + param_env: ty::ParamEnv<'tcx>, + region_data: RegionConstraintData<'tcx>, + vid_to_region: FxIndexMap>, +) -> clean::Generics { + let tcx = cx.tcx; + let generics = tcx.generics_of(item_def_id); + + let params: ThinVec<_> = generics + .params + .iter() + .inspect(|param| { + if cfg!(debug_assertions) { + debug_assert!(!param.is_anonymous_lifetime() && !param.is_host_effect()); + if let ty::GenericParamDefKind::Type { synthetic, .. } = param.kind { + debug_assert!(!synthetic && param.name != kw::SelfUpper); } - new_generics } - AutoTraitResult::NegativeImpl => { - polarity = ty::ImplPolarity::Negative; - - // For negative impls, we use the generic params, but *not* the predicates, - // from the original type. Otherwise, the displayed impl appears to be a - // conditional negative impl, when it's really unconditional. - // - // For example, consider the struct Foo(*mut T). Using - // the original predicates in our impl would cause us to generate - // `impl !Send for Foo`, which makes it appear that Foo - // implements Send where T is not copy. - // - // Instead, we generate `impl !Send for Foo`, which better - // expresses the fact that `Foo` never implements `Send`, - // regardless of the choice of `T`. - let raw_generics = clean_ty_generics( - self.cx, - tcx.generics_of(item_def_id), - ty::GenericPredicates::default(), - ); - let params = raw_generics.params; - - Generics { params, where_predicates: ThinVec::new() } - } - AutoTraitResult::ExplicitImpl => return None, - }; - - Some(Item { - name: None, - attrs: Default::default(), - item_id: ItemId::Auto { trait_: trait_def_id, for_: item_def_id }, - kind: Box::new(ImplItem(Box::new(Impl { - unsafety: hir::Unsafety::Normal, - generics: new_generics, - trait_: Some(clean_trait_ref_with_bindings(self.cx, trait_ref, ThinVec::new())), - for_: clean_middle_ty(ty::Binder::dummy(ty), self.cx, None, None), - items: Vec::new(), - polarity, - kind: ImplKind::Auto, - }))), - cfg: None, - inline_stmt_id: None, }) - } - - pub(crate) fn get_auto_trait_impls(&mut self, item_def_id: DefId) -> Vec { - let tcx = self.cx.tcx; - let param_env = tcx.param_env(item_def_id); - let ty = tcx.type_of(item_def_id).instantiate_identity(); - let f = auto_trait::AutoTraitFinder::new(tcx); - - debug!("get_auto_trait_impls({ty:?})"); - let auto_traits: Vec<_> = self.cx.auto_traits.to_vec(); - let mut auto_traits: Vec = auto_traits - .into_iter() - .filter_map(|trait_def_id| { - self.generate_for_trait(ty, trait_def_id, param_env, item_def_id, &f, false) - }) - .collect(); - // We are only interested in case the type *doesn't* implement the Sized trait. - if !ty.is_sized(tcx, param_env) { - // In case `#![no_core]` is used, `sized_trait` returns nothing. - if let Some(item) = tcx.lang_items().sized_trait().and_then(|sized_trait_did| { - self.generate_for_trait(ty, sized_trait_did, param_env, item_def_id, &f, true) - }) { - auto_traits.push(item); - } - } - auto_traits - } - - fn get_lifetime(region: Region<'_>, names_map: &FxHashMap) -> Lifetime { - region_name(region) - .map(|name| { - names_map - .get(&name) - .unwrap_or_else(|| panic!("Missing lifetime with name {name:?} for {region:?}")) + // We're basing the generics of the synthetic auto trait impl off of the generics of the + // implementing type. Its generic parameters may have defaults, don't copy them over: + // Generic parameter defaults are meaningless in impls. + .map(|param| clean_generic_param_def(param, clean::ParamDefaults::No, cx)) + .collect(); + + // FIXME(#111101): Incorporate the explicit predicates of the item here... + let item_predicates: FxIndexSet<_> = + tcx.predicates_of(item_def_id).predicates.iter().map(|(pred, _)| pred).collect(); + let where_predicates = param_env + .caller_bounds() + .iter() + // FIXME: ...which hopefully allows us to simplify this: + .filter(|pred| { + !item_predicates.contains(pred) + || pred + .as_trait_clause() + .is_some_and(|pred| tcx.lang_items().sized_trait() == Some(pred.def_id())) + }) + .map(|pred| { + tcx.fold_regions(pred, |r, _| match *r { + // FIXME: Don't `unwrap_or`, I think we should panic if we encounter an infer var that + // we can't map to a concrete region. However, `AutoTraitFinder` *does* leak those kinds + // of `ReVar`s for some reason at the time of writing. See `rustdoc-ui/` tests. + // This is in dire need of an investigation into `AutoTraitFinder`. + ty::ReVar(vid) => vid_to_region.get(&vid).copied().unwrap_or(r), + ty::ReEarlyParam(_) | ty::ReStatic | ty::ReBound(..) | ty::ReError(_) => r, + // FIXME(#120606): `AutoTraitFinder` can actually leak placeholder regions which feels + // incorrect. Needs investigation. + ty::ReLateParam(_) | ty::RePlaceholder(_) | ty::ReErased => { + bug!("unexpected region kind: {r:?}") + } }) - .unwrap_or(&Lifetime::statik()) - .clone() - } - - /// This method calculates two things: Lifetime constraints of the form `'a: 'b`, - /// and region constraints of the form `RegionVid: 'a` - /// - /// This is essentially a simplified version of lexical_region_resolve. However, - /// handle_lifetimes determines what *needs be* true in order for an impl to hold. - /// lexical_region_resolve, along with much of the rest of the compiler, is concerned - /// with determining if a given set up constraints/predicates *are* met, given some - /// starting conditions (e.g., user-provided code). For this reason, it's easier - /// to perform the calculations we need on our own, rather than trying to make - /// existing inference/solver code do what we want. - fn handle_lifetimes<'cx>( - regions: &RegionConstraintData<'cx>, - names_map: &FxHashMap, - ) -> ThinVec { - // Our goal is to 'flatten' the list of constraints by eliminating - // all intermediate RegionVids. At the end, all constraints should - // be between Regions (aka region variables). This gives us the information - // we need to create the Generics. - let mut finished: FxHashMap<_, Vec<_>> = Default::default(); - - let mut vid_map: FxHashMap, RegionDeps<'_>> = Default::default(); - - // Flattening is done in two parts. First, we insert all of the constraints - // into a map. Each RegionTarget (either a RegionVid or a Region) maps - // to its smaller and larger regions. Note that 'larger' regions correspond - // to sub-regions in Rust code (e.g., in 'a: 'b, 'a is the larger region). - for (constraint, _) in ®ions.constraints { - match *constraint { - Constraint::VarSubVar(r1, r2) => { - { - let deps1 = vid_map.entry(RegionTarget::RegionVid(r1)).or_default(); - deps1.larger.insert(RegionTarget::RegionVid(r2)); - } + }) + .flat_map(|pred| clean_predicate(pred, cx)) + .chain(clean_region_outlives_constraints(®ion_data, generics)) + .collect(); + + let mut generics = clean::Generics { params, where_predicates }; + simplify::sized_bounds(cx, &mut generics); + generics.where_predicates = simplify::where_clauses(cx, generics.where_predicates); + generics +} - let deps2 = vid_map.entry(RegionTarget::RegionVid(r2)).or_default(); - deps2.smaller.insert(RegionTarget::RegionVid(r1)); - } - Constraint::RegSubVar(region, vid) => { - let deps = vid_map.entry(RegionTarget::RegionVid(vid)).or_default(); - deps.smaller.insert(RegionTarget::Region(region)); - } - Constraint::VarSubReg(vid, region) => { - let deps = vid_map.entry(RegionTarget::RegionVid(vid)).or_default(); - deps.larger.insert(RegionTarget::Region(region)); - } - Constraint::RegSubReg(r1, r2) => { - // The constraint is already in the form that we want, so we're done with it - // Desired order is 'larger, smaller', so flip then - if region_name(r1) != region_name(r2) { - finished - .entry(region_name(r2).expect("no region_name found")) - .or_default() - .push(r1); - } - } +/// Clean region outlives constraints to where-predicates. +/// +/// This is essentially a simplified version of `lexical_region_resolve`. +/// +/// However, here we determine what *needs to be* true in order for an impl to hold. +/// `lexical_region_resolve`, along with much of the rest of the compiler, is concerned +/// with determining if a given set up constraints / predicates *are* met, given some +/// starting conditions like user-provided code. +/// +/// For this reason, it's easier to perform the calculations we need on our own, +/// rather than trying to make existing inference/solver code do what we want. +fn clean_region_outlives_constraints<'tcx>( + regions: &RegionConstraintData<'tcx>, + generics: &'tcx ty::Generics, +) -> ThinVec { + // Our goal is to "flatten" the list of constraints by eliminating all intermediate + // `RegionVids` (region inference variables). At the end, all constraints should be + // between `Region`s. This gives us the information we need to create the where-predicates. + // This flattening is done in two parts. + + let mut outlives_predicates = FxIndexMap::<_, Vec<_>>::default(); + let mut map = FxIndexMap::, auto_trait::RegionDeps<'_>>::default(); + + // (1) We insert all of the constraints into a map. + // Each `RegionTarget` (a `RegionVid` or a `Region`) maps to its smaller and larger regions. + // Note that "larger" regions correspond to sub regions in the surface language. + // E.g., in `'a: 'b`, `'a` is the larger region. + for (constraint, _) in ®ions.constraints { + match *constraint { + Constraint::VarSubVar(vid1, vid2) => { + let deps1 = map.entry(RegionTarget::RegionVid(vid1)).or_default(); + deps1.larger.insert(RegionTarget::RegionVid(vid2)); + + let deps2 = map.entry(RegionTarget::RegionVid(vid2)).or_default(); + deps2.smaller.insert(RegionTarget::RegionVid(vid1)); } - } - - // Here, we 'flatten' the map one element at a time. - // All of the element's sub and super regions are connected - // to each other. For example, if we have a graph that looks like this: - // - // (A, B) - C - (D, E) - // Where (A, B) are subregions, and (D,E) are super-regions - // - // then after deleting 'C', the graph will look like this: - // ... - A - (D, E ...) - // ... - B - (D, E, ...) - // (A, B, ...) - D - ... - // (A, B, ...) - E - ... - // - // where '...' signifies the existing sub and super regions of an entry - // When two adjacent ty::Regions are encountered, we've computed a final - // constraint, and add it to our list. Since we make sure to never re-add - // deleted items, this process will always finish. - while !vid_map.is_empty() { - let target = *vid_map.keys().next().expect("Keys somehow empty"); - let deps = vid_map.remove(&target).expect("Entry somehow missing"); - - for smaller in deps.smaller.iter() { - for larger in deps.larger.iter() { - match (smaller, larger) { - (&RegionTarget::Region(r1), &RegionTarget::Region(r2)) => { - if region_name(r1) != region_name(r2) { - finished - .entry(region_name(r2).expect("no region name found")) - .or_default() - .push(r1) // Larger, smaller - } - } - (&RegionTarget::RegionVid(_), &RegionTarget::Region(_)) => { - if let Entry::Occupied(v) = vid_map.entry(*smaller) { - let smaller_deps = v.into_mut(); - smaller_deps.larger.insert(*larger); - smaller_deps.larger.remove(&target); - } - } - (&RegionTarget::Region(_), &RegionTarget::RegionVid(_)) => { - if let Entry::Occupied(v) = vid_map.entry(*larger) { - let deps = v.into_mut(); - deps.smaller.insert(*smaller); - deps.smaller.remove(&target); - } - } - (&RegionTarget::RegionVid(_), &RegionTarget::RegionVid(_)) => { - if let Entry::Occupied(v) = vid_map.entry(*smaller) { - let smaller_deps = v.into_mut(); - smaller_deps.larger.insert(*larger); - smaller_deps.larger.remove(&target); - } - - if let Entry::Occupied(v) = vid_map.entry(*larger) { - let larger_deps = v.into_mut(); - larger_deps.smaller.insert(*smaller); - larger_deps.smaller.remove(&target); - } - } - } + Constraint::RegSubVar(region, vid) => { + let deps = map.entry(RegionTarget::RegionVid(vid)).or_default(); + deps.smaller.insert(RegionTarget::Region(region)); + } + Constraint::VarSubReg(vid, region) => { + let deps = map.entry(RegionTarget::RegionVid(vid)).or_default(); + deps.larger.insert(RegionTarget::Region(region)); + } + Constraint::RegSubReg(r1, r2) => { + // The constraint is already in the form that we want, so we're done with it + // The desired order is [larger, smaller], so flip them. + if early_bound_region_name(r1) != early_bound_region_name(r2) { + outlives_predicates + .entry(early_bound_region_name(r2).expect("no region_name found")) + .or_default() + .push(r1); } } } - - let lifetime_predicates = names_map - .iter() - .flat_map(|(name, lifetime)| { - let empty = Vec::new(); - let bounds: FxHashSet = finished - .get(name) - .unwrap_or(&empty) - .iter() - .map(|region| GenericBound::Outlives(Self::get_lifetime(*region, names_map))) - .collect(); - - if bounds.is_empty() { - return None; - } - Some(WherePredicate::RegionPredicate { - lifetime: lifetime.clone(), - bounds: bounds.into_iter().collect(), - }) - }) - .collect(); - - lifetime_predicates } - fn extract_for_generics(&self, pred: ty::Clause<'tcx>) -> FxHashSet { - let bound_predicate = pred.kind(); - let tcx = self.cx.tcx; - let regions = - match bound_predicate.skip_binder() { - ty::ClauseKind::Trait(poly_trait_pred) => tcx - .collect_referenced_late_bound_regions(bound_predicate.rebind(poly_trait_pred)), - ty::ClauseKind::Projection(poly_proj_pred) => tcx - .collect_referenced_late_bound_regions(bound_predicate.rebind(poly_proj_pred)), - _ => return FxHashSet::default(), - }; - - regions - .into_iter() - .filter_map(|br| { - match br { - // We only care about named late bound regions, as we need to add them - // to the 'for<>' section - ty::BrNamed(def_id, name) => Some(GenericParamDef::lifetime(def_id, name)), - _ => None, - } - }) - .collect() - } - - fn make_final_bounds( - &self, - ty_to_bounds: FxHashMap>, - ty_to_fn: FxHashMap)>, - lifetime_to_bounds: FxHashMap>, - ) -> Vec { - ty_to_bounds - .into_iter() - .flat_map(|(ty, mut bounds)| { - if let Some((ref poly_trait, ref output)) = ty_to_fn.get(&ty) { - let mut new_path = poly_trait.trait_.clone(); - let last_segment = new_path.segments.pop().expect("segments were empty"); - - let (old_input, old_output) = match last_segment.args { - GenericArgs::AngleBracketed { args, .. } => { - let types = args - .iter() - .filter_map(|arg| match arg { - GenericArg::Type(ty) => Some(ty.clone()), - _ => None, - }) - .collect(); - (types, None) + // (2) Here, we "flatten" the map one element at a time. All of the elements' sub and super + // regions are connected to each other. For example, if we have a graph that looks like this: + // + // (A, B) - C - (D, E) + // + // where (A, B) are sub regions, and (D,E) are super regions. + // Then, after deleting 'C', the graph will look like this: + // + // ... - A - (D, E, ...) + // ... - B - (D, E, ...) + // (A, B, ...) - D - ... + // (A, B, ...) - E - ... + // + // where '...' signifies the existing sub and super regions of an entry. When two adjacent + // `Region`s are encountered, we've computed a final constraint, and add it to our list. + // Since we make sure to never re-add deleted items, this process will always finish. + while !map.is_empty() { + let target = *map.keys().next().unwrap(); + let deps = map.swap_remove(&target).unwrap(); + + for smaller in &deps.smaller { + for larger in &deps.larger { + match (smaller, larger) { + (&RegionTarget::Region(smaller), &RegionTarget::Region(larger)) => { + if early_bound_region_name(smaller) != early_bound_region_name(larger) { + outlives_predicates + .entry( + early_bound_region_name(larger).expect("no region name found"), + ) + .or_default() + .push(smaller) } - GenericArgs::Parenthesized { inputs, output } => (inputs, output), - }; - - let output = output.as_ref().cloned().map(Box::new); - if old_output.is_some() && old_output != output { - panic!("Output mismatch for {ty:?} {old_output:?} {output:?}"); - } - - let new_params = GenericArgs::Parenthesized { inputs: old_input, output }; - - new_path - .segments - .push(PathSegment { name: last_segment.name, args: new_params }); - - bounds.insert(GenericBound::TraitBound( - PolyTrait { - trait_: new_path, - generic_params: poly_trait.generic_params.clone(), - }, - hir::TraitBoundModifier::None, - )); - } - if bounds.is_empty() { - return None; - } - - let mut bounds_vec = bounds.into_iter().collect(); - self.sort_where_bounds(&mut bounds_vec); - - Some(WherePredicate::BoundPredicate { - ty, - bounds: bounds_vec, - bound_params: Vec::new(), - }) - }) - .chain(lifetime_to_bounds.into_iter().filter(|(_, bounds)| !bounds.is_empty()).map( - |(lifetime, bounds)| { - let mut bounds_vec = bounds.into_iter().collect(); - self.sort_where_bounds(&mut bounds_vec); - WherePredicate::RegionPredicate { lifetime, bounds: bounds_vec } - }, - )) - .collect() - } - - /// Converts the calculated `ParamEnv` and lifetime information to a [`clean::Generics`](Generics), suitable for - /// display on the docs page. Cleaning the `Predicates` produces sub-optimal [`WherePredicate`]s, - /// so we fix them up: - /// - /// * Multiple bounds for the same type are coalesced into one: e.g., `T: Copy`, `T: Debug` - /// becomes `T: Copy + Debug` - /// * `Fn` bounds are handled specially - instead of leaving it as `T: Fn(), = - /// K`, we use the dedicated syntax `T: Fn() -> K` - /// * We explicitly add a `?Sized` bound if we didn't find any `Sized` predicates for a type - fn param_env_to_generics( - &mut self, - item_def_id: DefId, - param_env: ty::ParamEnv<'tcx>, - mut existing_predicates: ThinVec, - vid_to_region: FxHashMap>, - ) -> Generics { - debug!( - "param_env_to_generics(item_def_id={:?}, param_env={:?}, \ - existing_predicates={:?})", - item_def_id, param_env, existing_predicates - ); - - let tcx = self.cx.tcx; - - // The `Sized` trait must be handled specially, since we only display it when - // it is *not* required (i.e., '?Sized') - let sized_trait = tcx.require_lang_item(LangItem::Sized, None); - - let mut replacer = RegionReplacer { vid_to_region: &vid_to_region, tcx }; - - let orig_bounds: FxHashSet<_> = tcx.param_env(item_def_id).caller_bounds().iter().collect(); - let clean_where_predicates = param_env - .caller_bounds() - .iter() - .filter(|p| { - !orig_bounds.contains(p) - || match p.kind().skip_binder() { - ty::ClauseKind::Trait(pred) => pred.def_id() == sized_trait, - _ => false, - } - }) - .map(|p| p.fold_with(&mut replacer)); - - let raw_generics = clean_ty_generics( - self.cx, - tcx.generics_of(item_def_id), - tcx.explicit_predicates_of(item_def_id), - ); - let mut generic_params = raw_generics.params; - - debug!("param_env_to_generics({item_def_id:?}): generic_params={generic_params:?}"); - - let mut has_sized = FxHashSet::default(); - let mut ty_to_bounds: FxHashMap<_, FxHashSet<_>> = Default::default(); - let mut lifetime_to_bounds: FxHashMap<_, FxHashSet<_>> = Default::default(); - let mut ty_to_traits: FxHashMap> = Default::default(); - - let mut ty_to_fn: FxHashMap)> = Default::default(); - - // FIXME: This code shares much of the logic found in `clean_ty_generics` and - // `simplify::where_clause`. Consider deduplicating it to avoid diverging - // implementations. - // Further, the code below does not merge (partially re-sugared) bounds like - // `Tr` & `Tr` and it does not render higher-ranked parameters - // originating from equality predicates. - for p in clean_where_predicates { - let (orig_p, p) = (p, clean_predicate(p, self.cx)); - if p.is_none() { - continue; - } - let p = p.unwrap(); - match p { - WherePredicate::BoundPredicate { ty, mut bounds, .. } => { - // Writing a projection trait bound of the form - // ::Name : ?Sized - // is illegal, because ?Sized bounds can only - // be written in the (here, nonexistent) definition - // of the type. - // Therefore, we make sure that we never add a ?Sized - // bound for projections - if let Type::QPath { .. } = ty { - has_sized.insert(ty.clone()); } - - if bounds.is_empty() { - continue; - } - - let mut for_generics = self.extract_for_generics(orig_p); - - assert!(bounds.len() == 1); - let mut b = bounds.pop().expect("bounds were empty"); - - if b.is_sized_bound(self.cx) { - has_sized.insert(ty.clone()); - } else if !b - .get_trait_path() - .and_then(|trait_| { - ty_to_traits - .get(&ty) - .map(|bounds| bounds.contains(&strip_path_generics(trait_))) - }) - .unwrap_or(false) - { - // If we've already added a projection bound for the same type, don't add - // this, as it would be a duplicate - - // Handle any 'Fn/FnOnce/FnMut' bounds specially, - // as we want to combine them with any 'Output' qpaths - // later - - let is_fn = match b { - GenericBound::TraitBound(ref mut p, _) => { - // Insert regions into the for_generics hash map first, to ensure - // that we don't end up with duplicate bounds (e.g., for<'b, 'b>) - for_generics.extend(p.generic_params.drain(..)); - p.generic_params.extend(for_generics); - self.is_fn_trait(&p.trait_) - } - _ => false, - }; - - let poly_trait = b.get_poly_trait().expect("Cannot get poly trait"); - - if is_fn { - ty_to_fn - .entry(ty.clone()) - .and_modify(|e| *e = (poly_trait.clone(), e.1.clone())) - .or_insert(((poly_trait.clone()), None)); - - ty_to_bounds.entry(ty.clone()).or_default(); - } else { - ty_to_bounds.entry(ty.clone()).or_default().insert(b.clone()); + (&RegionTarget::RegionVid(_), &RegionTarget::Region(_)) => { + if let IndexEntry::Occupied(v) = map.entry(*smaller) { + let smaller_deps = v.into_mut(); + smaller_deps.larger.insert(*larger); + smaller_deps.larger.swap_remove(&target); } } - } - WherePredicate::RegionPredicate { lifetime, bounds } => { - lifetime_to_bounds.entry(lifetime).or_default().extend(bounds); - } - WherePredicate::EqPredicate { lhs, rhs } => { - match lhs { - Type::QPath(box QPathData { - ref assoc, - ref self_type, - trait_: Some(ref trait_), - .. - }) => { - let ty = &*self_type; - let mut new_trait = trait_.clone(); - - if self.is_fn_trait(trait_) && assoc.name == sym::Output { - ty_to_fn - .entry(ty.clone()) - .and_modify(|e| { - *e = (e.0.clone(), Some(rhs.ty().unwrap().clone())) - }) - .or_insert(( - PolyTrait { - trait_: trait_.clone(), - generic_params: Vec::new(), - }, - Some(rhs.ty().unwrap().clone()), - )); - continue; - } - - let args = &mut new_trait - .segments - .last_mut() - .expect("segments were empty") - .args; - - match args { - // Convert something like ' = u8' - // to 'T: Iterator' - GenericArgs::AngleBracketed { ref mut bindings, .. } => { - bindings.push(TypeBinding { - assoc: assoc.clone(), - kind: TypeBindingKind::Equality { term: rhs }, - }); - } - GenericArgs::Parenthesized { .. } => { - existing_predicates.push(WherePredicate::EqPredicate { - lhs: lhs.clone(), - rhs, - }); - continue; // If something other than a Fn ends up - // with parentheses, leave it alone - } - } - - let bounds = ty_to_bounds.entry(ty.clone()).or_default(); - - bounds.insert(GenericBound::TraitBound( - PolyTrait { trait_: new_trait, generic_params: Vec::new() }, - hir::TraitBoundModifier::None, - )); - - // Remove any existing 'plain' bound (e.g., 'T: Iterator`) so - // that we don't see a - // duplicate bound like `T: Iterator + Iterator` - // on the docs page. - bounds.remove(&GenericBound::TraitBound( - PolyTrait { trait_: trait_.clone(), generic_params: Vec::new() }, - hir::TraitBoundModifier::None, - )); - // Avoid creating any new duplicate bounds later in the outer - // loop - ty_to_traits.entry(ty.clone()).or_default().insert(trait_.clone()); + (&RegionTarget::Region(_), &RegionTarget::RegionVid(_)) => { + if let IndexEntry::Occupied(v) = map.entry(*larger) { + let deps = v.into_mut(); + deps.smaller.insert(*smaller); + deps.smaller.swap_remove(&target); } - _ => panic!("Unexpected LHS {lhs:?} for {item_def_id:?}"), } - } - }; - } - - let final_bounds = self.make_final_bounds(ty_to_bounds, ty_to_fn, lifetime_to_bounds); - - existing_predicates.extend(final_bounds); - - for param in generic_params.iter_mut() { - match param.kind { - GenericParamDefKind::Type { ref mut default, ref mut bounds, .. } => { - // We never want something like `impl`. - default.take(); - let generic_ty = Type::Generic(param.name); - if !has_sized.contains(&generic_ty) { - bounds.insert(0, GenericBound::maybe_sized(self.cx)); + (&RegionTarget::RegionVid(_), &RegionTarget::RegionVid(_)) => { + if let IndexEntry::Occupied(v) = map.entry(*smaller) { + let smaller_deps = v.into_mut(); + smaller_deps.larger.insert(*larger); + smaller_deps.larger.swap_remove(&target); + } + if let IndexEntry::Occupied(v) = map.entry(*larger) { + let larger_deps = v.into_mut(); + larger_deps.smaller.insert(*smaller); + larger_deps.smaller.swap_remove(&target); + } } } - GenericParamDefKind::Lifetime { .. } => {} - GenericParamDefKind::Const { ref mut default, .. } => { - // We never want something like `impl` - default.take(); - } } } - - self.sort_where_predicates(&mut existing_predicates); - - Generics { params: generic_params, where_predicates: existing_predicates } - } - - /// Ensure that the predicates are in a consistent order. The precise - /// ordering doesn't actually matter, but it's important that - /// a given set of predicates always appears in the same order - - /// both for visual consistency between 'rustdoc' runs, and to - /// make writing tests much easier - #[inline] - fn sort_where_predicates(&self, predicates: &mut [WherePredicate]) { - // We should never have identical bounds - and if we do, - // they're visually identical as well. Therefore, using - // an unstable sort is fine. - self.unstable_debug_sort(predicates); - } - - /// Ensure that the bounds are in a consistent order. The precise - /// ordering doesn't actually matter, but it's important that - /// a given set of bounds always appears in the same order - - /// both for visual consistency between 'rustdoc' runs, and to - /// make writing tests much easier - #[inline] - fn sort_where_bounds(&self, bounds: &mut Vec) { - // We should never have identical bounds - and if we do, - // they're visually identical as well. Therefore, using - // an unstable sort is fine. - self.unstable_debug_sort(bounds); } - /// This might look horrendously hacky, but it's actually not that bad. - /// - /// For performance reasons, we use several different FxHashMaps - /// in the process of computing the final set of where predicates. - /// However, the iteration order of a HashMap is completely unspecified. - /// In fact, the iteration of an FxHashMap can even vary between platforms, - /// since FxHasher has different behavior for 32-bit and 64-bit platforms. - /// - /// Obviously, it's extremely undesirable for documentation rendering - /// to be dependent on the platform it's run on. Apart from being confusing - /// to end users, it makes writing tests much more difficult, as predicates - /// can appear in any order in the final result. - /// - /// To solve this problem, we sort WherePredicates and GenericBounds - /// by their Debug string. The thing to keep in mind is that we don't really - /// care what the final order is - we're synthesizing an impl or bound - /// ourselves, so any order can be considered equally valid. By sorting the - /// predicates and bounds, however, we ensure that for a given codebase, all - /// auto-trait impls always render in exactly the same way. - /// - /// Using the Debug implementation for sorting prevents us from needing to - /// write quite a bit of almost entirely useless code (e.g., how should two - /// Types be sorted relative to each other). It also allows us to solve the - /// problem for both WherePredicates and GenericBounds at the same time. This - /// approach is probably somewhat slower, but the small number of items - /// involved (impls rarely have more than a few bounds) means that it - /// shouldn't matter in practice. - fn unstable_debug_sort(&self, vec: &mut [T]) { - vec.sort_by_cached_key(|x| format!("{x:?}")) - } + let region_params: FxIndexSet<_> = generics + .params + .iter() + .filter_map(|param| match param.kind { + ty::GenericParamDefKind::Lifetime => Some(param.name), + _ => None, + }) + .collect(); - fn is_fn_trait(&self, path: &Path) -> bool { - let tcx = self.cx.tcx; - let did = path.def_id(); - did == tcx.require_lang_item(LangItem::Fn, None) - || did == tcx.require_lang_item(LangItem::FnMut, None) - || did == tcx.require_lang_item(LangItem::FnOnce, None) - } + region_params + .iter() + .filter_map(|&name| { + let bounds: FxIndexSet<_> = outlives_predicates + .get(&name)? + .iter() + .map(|®ion| { + let lifetime = early_bound_region_name(region) + .inspect(|name| assert!(region_params.contains(name))) + .map(|name| Lifetime(name)) + .unwrap_or(Lifetime::statik()); + clean::GenericBound::Outlives(lifetime) + }) + .collect(); + if bounds.is_empty() { + return None; + } + Some(clean::WherePredicate::RegionPredicate { + lifetime: Lifetime(name), + bounds: bounds.into_iter().collect(), + }) + }) + .collect() } -fn region_name(region: Region<'_>) -> Option { +fn early_bound_region_name(region: Region<'_>) -> Option { match *region { ty::ReEarlyParam(r) => Some(r.name), _ => None, } } - -/// Replaces all [`ty::RegionVid`]s in a type with [`ty::Region`]s, using the provided map. -struct RegionReplacer<'a, 'tcx> { - vid_to_region: &'a FxHashMap>, - tcx: TyCtxt<'tcx>, -} - -impl<'a, 'tcx> TypeFolder> for RegionReplacer<'a, 'tcx> { - fn interner(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - match *r { - // These are the regions that can be seen in the AST. - ty::ReVar(vid) => self.vid_to_region.get(&vid).cloned().unwrap_or(r), - ty::ReEarlyParam(_) | ty::ReStatic | ty::ReBound(..) | ty::ReError(_) => r, - r => bug!("unexpected region: {r:?}"), - } - } -} diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index 47cfe651e319d..72d4cc7c4659c 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -47,7 +47,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { // Require the type the impl is implemented on to match // our type, and ignore the impl if there was a mismatch. let Ok(eq_result) = infcx.at(&traits::ObligationCause::dummy(), param_env).eq( - DefineOpaqueTypes::No, + DefineOpaqueTypes::Yes, impl_trait_ref.self_ty(), impl_ty, ) else { diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 0cdf52bfb0046..a25a506d9c5b9 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -21,10 +21,8 @@ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId, LOCAL_CRATE}; use rustc_hir::PredicateOrigin; use rustc_hir_analysis::lower_ty; -use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData}; use rustc_middle::metadata::Reexport; use rustc_middle::middle::resolve_bound_vars as rbv; -use rustc_middle::ty::fold::TypeFolder; use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, AdtKind, Ty, TyCtxt}; @@ -35,9 +33,7 @@ use rustc_span::{self, ExpnKind}; use rustc_trait_selection::traits::wf::object_region_bounds; use std::borrow::Cow; -use std::collections::hash_map::Entry; use std::collections::BTreeMap; -use std::hash::Hash; use std::mem; use thin_vec::ThinVec; @@ -502,6 +498,7 @@ fn projection_to_path_segment<'tcx>( fn clean_generic_param_def<'tcx>( def: &ty::GenericParamDef, + defaults: ParamDefaults, cx: &mut DocContext<'tcx>, ) -> GenericParamDef { let (name, kind) = match def.kind { @@ -509,7 +506,9 @@ fn clean_generic_param_def<'tcx>( (def.name, GenericParamDefKind::Lifetime { outlives: ThinVec::new() }) } ty::GenericParamDefKind::Type { has_default, synthetic, .. } => { - let default = if has_default { + let default = if let ParamDefaults::Yes = defaults + && has_default + { Some(clean_middle_ty( ty::Binder::dummy(cx.tcx.type_of(def.def_id).instantiate_identity()), cx, @@ -542,11 +541,14 @@ fn clean_generic_param_def<'tcx>( Some(def.def_id), None, )), - default: match has_default { - true => Some(Box::new( + default: if let ParamDefaults::Yes = defaults + && has_default + { + Some(Box::new( cx.tcx.const_param_default(def.def_id).instantiate_identity().to_string(), - )), - false => None, + )) + } else { + None }, is_host_effect, }, @@ -556,6 +558,12 @@ fn clean_generic_param_def<'tcx>( GenericParamDef { name, def_id: def.def_id, kind } } +/// Whether to clean generic parameter defaults or not. +enum ParamDefaults { + Yes, + No, +} + fn clean_generic_param<'tcx>( cx: &mut DocContext<'tcx>, generics: Option<&hir::Generics<'tcx>>, @@ -759,34 +767,30 @@ fn clean_ty_generics<'tcx>( gens: &ty::Generics, preds: ty::GenericPredicates<'tcx>, ) -> Generics { - // Don't populate `cx.impl_trait_bounds` before `clean`ning `where` clauses, - // since `Clean for ty::Predicate` would consume them. + // Don't populate `cx.impl_trait_bounds` before cleaning where clauses, + // since `clean_predicate` would consume them. let mut impl_trait = BTreeMap::>::default(); - // Bounds in the type_params and lifetimes fields are repeated in the - // predicates field (see rustc_hir_analysis::collect::ty_generics), so remove - // them. - let stripped_params = gens + let params: ThinVec<_> = gens .params .iter() - .filter_map(|param| match param.kind { - ty::GenericParamDefKind::Lifetime if param.is_anonymous_lifetime() => None, - ty::GenericParamDefKind::Lifetime => Some(clean_generic_param_def(param, cx)), + .filter(|param| match param.kind { + ty::GenericParamDefKind::Lifetime => !param.is_anonymous_lifetime(), ty::GenericParamDefKind::Type { synthetic, .. } => { if param.name == kw::SelfUpper { - assert_eq!(param.index, 0); - return None; + debug_assert_eq!(param.index, 0); + return false; } if synthetic { impl_trait.insert(param.index, vec![]); - return None; + return false; } - Some(clean_generic_param_def(param, cx)) + true } - ty::GenericParamDefKind::Const { is_host_effect: true, .. } => None, - ty::GenericParamDefKind::Const { .. } => Some(clean_generic_param_def(param, cx)), + ty::GenericParamDefKind::Const { is_host_effect, .. } => !is_host_effect, }) - .collect::>(); + .map(|param| clean_generic_param_def(param, ParamDefaults::Yes, cx)) + .collect(); // param index -> [(trait DefId, associated type name & generics, term)] let mut impl_trait_proj = @@ -882,56 +886,13 @@ fn clean_ty_generics<'tcx>( // Now that `cx.impl_trait_bounds` is populated, we can process // remaining predicates which could contain `impl Trait`. - let mut where_predicates = - where_predicates.into_iter().flat_map(|p| clean_predicate(*p, cx)).collect::>(); - - // In the surface language, all type parameters except `Self` have an - // implicit `Sized` bound unless removed with `?Sized`. - // However, in the list of where-predicates below, `Sized` appears like a - // normal bound: It's either present (the type is sized) or - // absent (the type might be unsized) but never *maybe* (i.e. `?Sized`). - // - // This is unsuitable for rendering. - // Thus, as a first step remove all `Sized` bounds that should be implicit. - // - // Note that associated types also have an implicit `Sized` bound but we - // don't actually know the set of associated types right here so that's - // handled when cleaning associated types. - let mut sized_params = FxHashSet::default(); - where_predicates.retain(|pred| { - if let WherePredicate::BoundPredicate { ty: Generic(g), bounds, .. } = pred - && *g != kw::SelfUpper - && bounds.iter().any(|b| b.is_sized_bound(cx)) - { - sized_params.insert(*g); - false - } else { - true - } - }); - - // As a final step, go through the type parameters again and insert a - // `?Sized` bound for each one we didn't find to be `Sized`. - for tp in &stripped_params { - if let types::GenericParamDefKind::Type { .. } = tp.kind - && !sized_params.contains(&tp.name) - { - where_predicates.push(WherePredicate::BoundPredicate { - ty: Type::Generic(tp.name), - bounds: vec![GenericBound::maybe_sized(cx)], - bound_params: Vec::new(), - }) - } - } - - // It would be nice to collect all of the bounds on a type and recombine - // them if possible, to avoid e.g., `where T: Foo, T: Bar, T: Sized, T: 'a` - // and instead see `where T: Foo + Bar + Sized + 'a` + let where_predicates = + where_predicates.into_iter().flat_map(|p| clean_predicate(*p, cx)).collect(); - Generics { - params: stripped_params, - where_predicates: simplify::where_clauses(cx, where_predicates), - } + let mut generics = Generics { params, where_predicates }; + simplify::sized_bounds(cx, &mut generics); + generics.where_predicates = simplify::where_clauses(cx, generics.where_predicates); + generics } fn clean_ty_alias_inner_type<'tcx>( diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs index c35fb9ec78875..5a3ccb6239a06 100644 --- a/src/librustdoc/clean/simplify.rs +++ b/src/librustdoc/clean/simplify.rs @@ -12,6 +12,7 @@ //! bounds by special casing scenarios such as these. Fun! use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::unord::UnordSet; use rustc_hir::def_id::DefId; use rustc_middle::ty; use thin_vec::ThinVec; @@ -21,7 +22,7 @@ use crate::clean::GenericArgs as PP; use crate::clean::WherePredicate as WP; use crate::core::DocContext; -pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: Vec) -> ThinVec { +pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: ThinVec) -> ThinVec { // First, partition the where clause into its separate components. // // We use `FxIndexMap` so that the insertion order is preserved to prevent messing up to @@ -128,6 +129,48 @@ fn trait_is_same_or_supertrait(cx: &DocContext<'_>, child: DefId, trait_: DefId) .any(|did| trait_is_same_or_supertrait(cx, did, trait_)) } +pub(crate) fn sized_bounds(cx: &mut DocContext<'_>, generics: &mut clean::Generics) { + let mut sized_params = UnordSet::new(); + + // In the surface language, all type parameters except `Self` have an + // implicit `Sized` bound unless removed with `?Sized`. + // However, in the list of where-predicates below, `Sized` appears like a + // normal bound: It's either present (the type is sized) or + // absent (the type might be unsized) but never *maybe* (i.e. `?Sized`). + // + // This is unsuitable for rendering. + // Thus, as a first step remove all `Sized` bounds that should be implicit. + // + // Note that associated types also have an implicit `Sized` bound but we + // don't actually know the set of associated types right here so that + // should be handled when cleaning associated types. + generics.where_predicates.retain(|pred| { + if let WP::BoundPredicate { ty: clean::Generic(param), bounds, .. } = pred + && *param != rustc_span::symbol::kw::SelfUpper + && bounds.iter().any(|b| b.is_sized_bound(cx)) + { + sized_params.insert(*param); + false + } else { + true + } + }); + + // As a final step, go through the type parameters again and insert a + // `?Sized` bound for each one we didn't find to be `Sized`. + for param in &generics.params { + if let clean::GenericParamDefKind::Type { .. } = param.kind + && !sized_params.contains(¶m.name) + { + generics.where_predicates.push(WP::BoundPredicate { + ty: clean::Type::Generic(param.name), + bounds: vec![clean::GenericBound::maybe_sized(cx)], + bound_params: Vec::new(), + }) + } + } +} + /// Move bounds that are (likely) directly attached to generic parameters from the where-clause to /// the respective parameter. /// diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index a51f6360df2a4..6793ea9f485f3 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1277,13 +1277,6 @@ impl GenericBound { false } - pub(crate) fn get_poly_trait(&self) -> Option { - if let GenericBound::TraitBound(ref p, _) = *self { - return Some(p.clone()); - } - None - } - pub(crate) fn get_trait_path(&self) -> Option { if let GenericBound::TraitBound(PolyTrait { ref trait_, .. }, _) = *self { Some(trait_.clone()) diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 977b4bb45b620..d5e0e83696f3f 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -1,4 +1,4 @@ -use crate::clean::auto_trait::AutoTraitFinder; +use crate::clean::auto_trait::synthesize_auto_trait_impls; use crate::clean::blanket_impl::BlanketImplFinder; use crate::clean::render_macro_matchers::render_macro_matcher; use crate::clean::{ @@ -251,15 +251,6 @@ pub(super) fn clean_middle_path<'tcx>( } } -/// Remove the generic arguments from a path. -pub(crate) fn strip_path_generics(mut path: Path) -> Path { - for ps in path.segments.iter_mut() { - ps.args = GenericArgs::AngleBracketed { args: Default::default(), bindings: ThinVec::new() } - } - - path -} - pub(crate) fn qpath_to_string(p: &hir::QPath<'_>) -> String { let segments = match *p { hir::QPath::Resolved(_, path) => &path.segments, @@ -486,6 +477,7 @@ pub(crate) fn resolve_type(cx: &mut DocContext<'_>, path: Path) -> Type { } } +// FIXME(fmease): Update the `get_*` terminology to the `synthesize_` one. pub(crate) fn get_auto_trait_and_blanket_impls( cx: &mut DocContext<'_>, item_def_id: DefId, @@ -493,8 +485,8 @@ pub(crate) fn get_auto_trait_and_blanket_impls( let auto_impls = cx .sess() .prof - .generic_activity("get_auto_trait_impls") - .run(|| AutoTraitFinder::new(cx).get_auto_trait_impls(item_def_id)); + .generic_activity("synthesize_auto_trait_impls") + .run(|| synthesize_auto_trait_impls(cx, item_def_id)); let blanket_impls = cx .sess() .prof diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index f1887684797a6..4e46f847fd76a 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -184,40 +184,15 @@ pub(crate) enum RenderTypeId { impl RenderTypeId { pub fn write_to_string(&self, string: &mut String) { - // (sign, value) - let (sign, id): (bool, u32) = match &self { + let id: i32 = match &self { // 0 is a sentinel, everything else is one-indexed // concrete type - RenderTypeId::Index(idx) if *idx >= 0 => (false, (idx + 1isize).try_into().unwrap()), + RenderTypeId::Index(idx) if *idx >= 0 => (idx + 1isize).try_into().unwrap(), // generic type parameter - RenderTypeId::Index(idx) => (true, (-*idx).try_into().unwrap()), + RenderTypeId::Index(idx) => (*idx).try_into().unwrap(), _ => panic!("must convert render types to indexes before serializing"), }; - // zig-zag encoding - let value: u32 = (id << 1) | (if sign { 1 } else { 0 }); - // Self-terminating hex use capital letters for everything but the - // least significant digit, which is lowercase. For example, decimal 17 - // would be `` Aa `` if zig-zag encoding weren't used. - // - // Zig-zag encoding, however, stores the sign bit as the last bit. - // This means, in the last hexit, 1 is actually `c`, -1 is `b` - // (`a` is the imaginary -0), and, because all the bits are shifted - // by one, `` A` `` is actually 8 and `` Aa `` is -8. - // - // https://rust-lang.github.io/rustc-dev-guide/rustdoc-internals/search.html - // describes the encoding in more detail. - let mut shift: u32 = 28; - let mut mask: u32 = 0xF0_00_00_00; - while shift < 32 { - let hexit = (value & mask) >> shift; - if hexit != 0 || shift == 0 { - let hex = - char::try_from(if shift == 0 { '`' } else { '@' } as u32 + hexit).unwrap(); - string.push(hex); - } - shift = shift.wrapping_sub(4); - mask = mask >> 4; - } + search_index::encode::write_vlqhex_to_string(id, string); } } @@ -2506,7 +2481,7 @@ fn render_call_locations(mut w: W, cx: &mut Context<'_>, item: &c // Look for the example file in the source map if it exists, otherwise return a dummy span let file_span = (|| { let source_map = tcx.sess.source_map(); - let crate_src = tcx.sess.local_crate_source_file()?; + let crate_src = tcx.sess.local_crate_source_file()?.into_local_path()?; let abs_crate_src = crate_src.canonicalize().ok()?; let crate_root = abs_crate_src.parent()?.parent()?; let rel_path = path.strip_prefix(crate_root).ok()?; diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index be2786c99ecab..51f90e455001e 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -1,3 +1,5 @@ +pub(crate) mod encode; + use std::collections::hash_map::Entry; use std::collections::{BTreeMap, VecDeque}; @@ -17,12 +19,46 @@ use crate::html::format::join_with_double_colon; use crate::html::markdown::short_markdown_summary; use crate::html::render::{self, IndexItem, IndexItemFunctionType, RenderType, RenderTypeId}; +use encode::{bitmap_to_string, write_vlqhex_to_string}; + +/// The serialized search description sharded version +/// +/// The `index` is a JSON-encoded list of names and other information. +/// +/// The desc has newlined descriptions, split up by size into 128KiB shards. +/// For example, `(4, "foo\nbar\nbaz\nquux")`. +/// +/// There is no single, optimal size for these shards, because it depends on +/// configuration values that we can't predict or control, such as the version +/// of HTTP used (HTTP/1.1 would work better with larger files, while HTTP/2 +/// and 3 are more agnostic), transport compression (gzip, zstd, etc), whether +/// the search query is going to produce a large number of results or a small +/// number, the bandwidth delay product of the network... +/// +/// Gzipping some standard library descriptions to guess what transport +/// compression will do, the compressed file sizes can be as small as 4.9KiB +/// or as large as 18KiB (ignoring the final 1.9KiB shard of leftovers). +/// A "reasonable" range for files is for them to be bigger than 1KiB, +/// since that's about the amount of data that can be transferred in a +/// single TCP packet, and 64KiB, the maximum amount of data that +/// TCP can transfer in a single round trip without extensions. +/// +/// [1]: https://en.wikipedia.org/wiki/Maximum_transmission_unit#MTUs_for_common_media +/// [2]: https://en.wikipedia.org/wiki/Sliding_window_protocol#Basic_concept +/// [3]: https://learn.microsoft.com/en-us/troubleshoot/windows-server/networking/description-tcp-features +pub(crate) struct SerializedSearchIndex { + pub(crate) index: String, + pub(crate) desc: Vec<(usize, String)>, +} + +const DESC_INDEX_SHARD_LEN: usize = 128 * 1024; + /// Builds the search index from the collected metadata pub(crate) fn build_index<'tcx>( krate: &clean::Crate, cache: &mut Cache, tcx: TyCtxt<'tcx>, -) -> String { +) -> SerializedSearchIndex { let mut itemid_to_pathid = FxHashMap::default(); let mut primitives = FxHashMap::default(); let mut associated_types = FxHashMap::default(); @@ -319,7 +355,6 @@ pub(crate) fn build_index<'tcx>( .collect::>(); struct CrateData<'a> { - doc: String, items: Vec<&'a IndexItem>, paths: Vec<(ItemType, Vec)>, // The String is alias name and the vec is the list of the elements with this alias. @@ -328,6 +363,11 @@ pub(crate) fn build_index<'tcx>( aliases: &'a BTreeMap>, // Used when a type has more than one impl with an associated item with the same name. associated_item_disambiguators: &'a Vec<(usize, String)>, + // A list of shard lengths encoded as vlqhex. See the comment in write_vlqhex_to_string + // for information on the format. + desc_index: String, + // A list of items with no description. This is eventually turned into a bitmap. + empty_desc: Vec, } struct Paths { @@ -409,7 +449,6 @@ pub(crate) fn build_index<'tcx>( let mut names = Vec::with_capacity(self.items.len()); let mut types = String::with_capacity(self.items.len()); let mut full_paths = Vec::with_capacity(self.items.len()); - let mut descriptions = Vec::with_capacity(self.items.len()); let mut parents = Vec::with_capacity(self.items.len()); let mut functions = String::with_capacity(self.items.len()); let mut deprecated = Vec::with_capacity(self.items.len()); @@ -432,7 +471,6 @@ pub(crate) fn build_index<'tcx>( parents.push(item.parent_idx.map(|x| x + 1).unwrap_or(0)); names.push(item.name.as_str()); - descriptions.push(&item.desc); if !item.path.is_empty() { full_paths.push((index, &item.path)); @@ -444,7 +482,8 @@ pub(crate) fn build_index<'tcx>( } if item.deprecation.is_some() { - deprecated.push(index); + // bitmasks always use 1-indexing for items, with 0 as the crate itself + deprecated.push(u32::try_from(index + 1).unwrap()); } } @@ -455,17 +494,16 @@ pub(crate) fn build_index<'tcx>( let has_aliases = !self.aliases.is_empty(); let mut crate_data = serializer.serialize_struct("CrateData", if has_aliases { 9 } else { 8 })?; - crate_data.serialize_field("doc", &self.doc)?; crate_data.serialize_field("t", &types)?; crate_data.serialize_field("n", &names)?; - // Serialize as an array of item indices and full paths crate_data.serialize_field("q", &full_paths)?; - crate_data.serialize_field("d", &descriptions)?; crate_data.serialize_field("i", &parents)?; crate_data.serialize_field("f", &functions)?; - crate_data.serialize_field("c", &deprecated)?; + crate_data.serialize_field("D", &self.desc_index)?; crate_data.serialize_field("p", &paths)?; crate_data.serialize_field("b", &self.associated_item_disambiguators)?; + crate_data.serialize_field("c", &bitmap_to_string(&deprecated))?; + crate_data.serialize_field("e", &bitmap_to_string(&self.empty_desc))?; if has_aliases { crate_data.serialize_field("a", &self.aliases)?; } @@ -473,16 +511,58 @@ pub(crate) fn build_index<'tcx>( } } - // Collect the index into a string - format!( + let (empty_desc, desc) = { + let mut empty_desc = Vec::new(); + let mut result = Vec::new(); + let mut set = String::new(); + let mut len: usize = 0; + let mut item_index: u32 = 0; + for desc in std::iter::once(&crate_doc).chain(crate_items.iter().map(|item| &item.desc)) { + if desc == "" { + empty_desc.push(item_index); + item_index += 1; + continue; + } + if set.len() >= DESC_INDEX_SHARD_LEN { + result.push((len, std::mem::replace(&mut set, String::new()))); + len = 0; + } else if len != 0 { + set.push('\n'); + } + set.push_str(&desc); + len += 1; + item_index += 1; + } + result.push((len, std::mem::replace(&mut set, String::new()))); + (empty_desc, result) + }; + + let desc_index = { + let mut desc_index = String::with_capacity(desc.len() * 4); + for &(len, _) in desc.iter() { + write_vlqhex_to_string(len.try_into().unwrap(), &mut desc_index); + } + desc_index + }; + + assert_eq!( + crate_items.len() + 1, + desc.iter().map(|(len, _)| *len).sum::() + empty_desc.len() + ); + + // The index, which is actually used to search, is JSON + // It uses `JSON.parse(..)` to actually load, since JSON + // parses faster than the full JavaScript syntax. + let index = format!( r#"["{}",{}]"#, krate.name(tcx), serde_json::to_string(&CrateData { - doc: crate_doc, items: crate_items, paths: crate_paths, aliases: &aliases, associated_item_disambiguators: &associated_item_disambiguators, + desc_index, + empty_desc, }) .expect("failed serde conversion") // All these `replace` calls are because we have to go through JS string for JSON content. @@ -490,7 +570,8 @@ pub(crate) fn build_index<'tcx>( .replace('\'', r"\'") // We need to escape double quotes for the JSON. .replace("\\\"", "\\\\\"") - ) + ); + SerializedSearchIndex { index, desc } } pub(crate) fn get_function_type_for_search<'tcx>( diff --git a/src/librustdoc/html/render/search_index/encode.rs b/src/librustdoc/html/render/search_index/encode.rs new file mode 100644 index 0000000000000..54407c614c4c7 --- /dev/null +++ b/src/librustdoc/html/render/search_index/encode.rs @@ -0,0 +1,243 @@ +use base64::prelude::*; + +pub(crate) fn write_vlqhex_to_string(n: i32, string: &mut String) { + let (sign, magnitude): (bool, u32) = + if n >= 0 { (false, n.try_into().unwrap()) } else { (true, (-n).try_into().unwrap()) }; + // zig-zag encoding + let value: u32 = (magnitude << 1) | (if sign { 1 } else { 0 }); + // Self-terminating hex use capital letters for everything but the + // least significant digit, which is lowercase. For example, decimal 17 + // would be `` Aa `` if zig-zag encoding weren't used. + // + // Zig-zag encoding, however, stores the sign bit as the last bit. + // This means, in the last hexit, 1 is actually `c`, -1 is `b` + // (`a` is the imaginary -0), and, because all the bits are shifted + // by one, `` A` `` is actually 8 and `` Aa `` is -8. + // + // https://rust-lang.github.io/rustc-dev-guide/rustdoc-internals/search.html + // describes the encoding in more detail. + let mut shift: u32 = 28; + let mut mask: u32 = 0xF0_00_00_00; + // first skip leading zeroes + while shift < 32 { + let hexit = (value & mask) >> shift; + if hexit != 0 || shift == 0 { + break; + } + shift = shift.wrapping_sub(4); + mask = mask >> 4; + } + // now write the rest + while shift < 32 { + let hexit = (value & mask) >> shift; + let hex = char::try_from(if shift == 0 { '`' } else { '@' } as u32 + hexit).unwrap(); + string.push(hex); + shift = shift.wrapping_sub(4); + mask = mask >> 4; + } +} + +// Used during bitmap encoding +enum Container { + /// number of ones, bits + Bits(Box<[u64; 1024]>), + /// list of entries + Array(Vec), + /// list of (start, len-1) + Run(Vec<(u16, u16)>), +} +impl Container { + fn popcount(&self) -> u32 { + match self { + Container::Bits(bits) => bits.iter().copied().map(|x| x.count_ones()).sum(), + Container::Array(array) => { + array.len().try_into().expect("array can't be bigger than 2**32") + } + Container::Run(runs) => { + runs.iter().copied().map(|(_, lenm1)| u32::from(lenm1) + 1).sum() + } + } + } + fn push(&mut self, value: u16) { + match self { + Container::Bits(bits) => bits[value as usize >> 6] |= 1 << (value & 0x3F), + Container::Array(array) => { + array.push(value); + if array.len() >= 4096 { + let array = std::mem::replace(array, Vec::new()); + *self = Container::Bits(Box::new([0; 1024])); + for value in array { + self.push(value); + } + } + } + Container::Run(runs) => { + if let Some(r) = runs.last_mut() + && r.0 + r.1 + 1 == value + { + r.1 += 1; + } else { + runs.push((value, 0)); + } + } + } + } + fn try_make_run(&mut self) -> bool { + match self { + Container::Bits(bits) => { + let mut r: u64 = 0; + for (i, chunk) in bits.iter().copied().enumerate() { + let next_chunk = + i.checked_add(1).and_then(|i| bits.get(i)).copied().unwrap_or(0); + r += !chunk & u64::from((chunk << 1).count_ones()); + r += !next_chunk & u64::from((chunk >> 63).count_ones()); + } + if (2 + 4 * r) >= 8192 { + return false; + } + let bits = std::mem::replace(bits, Box::new([0; 1024])); + *self = Container::Run(Vec::new()); + for (i, bits) in bits.iter().copied().enumerate() { + if bits == 0 { + continue; + } + for j in 0..64 { + let value = (u16::try_from(i).unwrap() << 6) | j; + if bits & (1 << j) != 0 { + self.push(value); + } + } + } + true + } + Container::Array(array) if array.len() <= 5 => false, + Container::Array(array) => { + let mut r = 0; + let mut prev = None; + for value in array.iter().copied() { + if value.checked_sub(1) != prev { + r += 1; + } + prev = Some(value); + } + if 2 + 4 * r >= 2 * array.len() + 2 { + return false; + } + let array = std::mem::replace(array, Vec::new()); + *self = Container::Run(Vec::new()); + for value in array { + self.push(value); + } + true + } + Container::Run(_) => true, + } + } +} + +// checked against roaring-rs in +// https://gitlab.com/notriddle/roaring-test +pub(crate) fn write_bitmap_to_bytes( + domain: &[u32], + mut out: impl std::io::Write, +) -> std::io::Result<()> { + // https://arxiv.org/pdf/1603.06549.pdf + let mut keys = Vec::::new(); + let mut containers = Vec::::new(); + let mut key: u16; + let mut domain_iter = domain.into_iter().copied().peekable(); + let mut has_run = false; + while let Some(entry) = domain_iter.next() { + key = (entry >> 16).try_into().expect("shifted off the top 16 bits, so it should fit"); + let value: u16 = (entry & 0x00_00_FF_FF).try_into().expect("AND 16 bits, so it should fit"); + let mut container = Container::Array(vec![value]); + while let Some(entry) = domain_iter.peek().copied() { + let entry_key: u16 = + (entry >> 16).try_into().expect("shifted off the top 16 bits, so it should fit"); + if entry_key != key { + break; + } + domain_iter.next().expect("peeking just succeeded"); + container + .push((entry & 0x00_00_FF_FF).try_into().expect("AND 16 bits, so it should fit")); + } + keys.push(key); + has_run = container.try_make_run() || has_run; + containers.push(container); + } + // https://github.com/RoaringBitmap/RoaringFormatSpec + use byteorder::{WriteBytesExt, LE}; + const SERIAL_COOKIE_NO_RUNCONTAINER: u32 = 12346; + const SERIAL_COOKIE: u32 = 12347; + const NO_OFFSET_THRESHOLD: u32 = 4; + let size: u32 = containers.len().try_into().unwrap(); + let start_offset = if has_run { + out.write_u32::(SERIAL_COOKIE | ((size - 1) << 16))?; + for set in containers.chunks(8) { + let mut b = 0; + for (i, container) in set.iter().enumerate() { + if matches!(container, &Container::Run(..)) { + b |= 1 << i; + } + } + out.write_u8(b)?; + } + if size < NO_OFFSET_THRESHOLD { + 4 + 4 * size + ((size + 7) / 8) + } else { + 4 + 8 * size + ((size + 7) / 8) + } + } else { + out.write_u32::(SERIAL_COOKIE_NO_RUNCONTAINER)?; + out.write_u32::(containers.len().try_into().unwrap())?; + 4 + 4 + 4 * size + 4 * size + }; + for (&key, container) in keys.iter().zip(&containers) { + // descriptive header + let key: u32 = key.into(); + let count: u32 = container.popcount() - 1; + out.write_u32::((count << 16) | key)?; + } + if !has_run || size >= NO_OFFSET_THRESHOLD { + // offset header + let mut starting_offset = start_offset; + for container in &containers { + out.write_u32::(starting_offset)?; + starting_offset += match container { + Container::Bits(_) => 8192u32, + Container::Array(array) => u32::try_from(array.len()).unwrap() * 2, + Container::Run(runs) => 2 + u32::try_from(runs.len()).unwrap() * 4, + }; + } + } + for container in &containers { + match container { + Container::Bits(bits) => { + for chunk in bits.iter() { + out.write_u64::(*chunk)?; + } + } + Container::Array(array) => { + for value in array.iter() { + out.write_u16::(*value)?; + } + } + Container::Run(runs) => { + out.write_u16::((runs.len()).try_into().unwrap())?; + for (start, lenm1) in runs.iter().copied() { + out.write_u16::(start)?; + out.write_u16::(lenm1)?; + } + } + } + } + Ok(()) +} + +pub(crate) fn bitmap_to_string(domain: &[u32]) -> String { + let mut buf = Vec::new(); + let mut strbuf = String::new(); + write_bitmap_to_bytes(&domain, &mut buf).unwrap(); + BASE64_STANDARD.encode_string(&buf, &mut strbuf); + strbuf +} diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index fbd45b2b48ef9..c806bf1cc66f3 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -24,6 +24,7 @@ use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; use crate::formats::Impl; use crate::html::format::Buffer; +use crate::html::render::search_index::SerializedSearchIndex; use crate::html::render::{AssocItemLink, ImplRenderingParameters}; use crate::html::{layout, static_files}; use crate::visit::DocVisitor; @@ -46,7 +47,7 @@ use crate::{try_err, try_none}; pub(super) fn write_shared( cx: &mut Context<'_>, krate: &Crate, - search_index: String, + search_index: SerializedSearchIndex, options: &RenderOptions, ) -> Result<(), Error> { // Write out the shared files. Note that these are shared among all rustdoc @@ -312,7 +313,7 @@ pub(super) fn write_shared( let dst = cx.dst.join(&format!("search-index{}.js", cx.shared.resource_suffix)); let (mut all_indexes, mut krates) = try_err!(collect_json(&dst, krate.name(cx.tcx()).as_str()), &dst); - all_indexes.push(search_index); + all_indexes.push(search_index.index); krates.push(krate.name(cx.tcx()).to_string()); krates.sort(); @@ -335,6 +336,32 @@ else if (window.initSearch) window.initSearch(searchIndex); Ok(v.into_bytes()) })?; + let search_desc_dir = cx.dst.join(format!("search.desc/{krate}", krate = krate.name(cx.tcx()))); + if Path::new(&search_desc_dir).exists() { + try_err!(std::fs::remove_dir_all(&search_desc_dir), &search_desc_dir); + } + try_err!(std::fs::create_dir_all(&search_desc_dir), &search_desc_dir); + let kratename = krate.name(cx.tcx()).to_string(); + for (i, (_, data)) in search_index.desc.into_iter().enumerate() { + let output_filename = static_files::suffix_path( + &format!("{kratename}-desc-{i}-.js"), + &cx.shared.resource_suffix, + ); + let path = search_desc_dir.join(output_filename); + try_err!( + std::fs::write( + &path, + &format!( + r##"searchState.loadedDescShard({kratename}, {i}, {data})"##, + kratename = serde_json::to_string(&kratename).unwrap(), + data = serde_json::to_string(&data).unwrap(), + ) + .into_bytes() + ), + &path + ); + } + write_invocation_specific("crates.js", &|| { let krates = krates.iter().map(|k| format!("\"{k}\"")).join(","); Ok(format!("window.ALL_CRATES = [{krates}];").into_bytes()) diff --git a/src/librustdoc/html/static/.eslintrc.js b/src/librustdoc/html/static/.eslintrc.js index 1a34530c2d16e..a1e9cc6dfa142 100644 --- a/src/librustdoc/html/static/.eslintrc.js +++ b/src/librustdoc/html/static/.eslintrc.js @@ -5,7 +5,7 @@ module.exports = { }, "extends": "eslint:recommended", "parserOptions": { - "ecmaVersion": 2015, + "ecmaVersion": 8, "sourceType": "module" }, "rules": { diff --git a/src/librustdoc/html/static/css/noscript.css b/src/librustdoc/html/static/css/noscript.css index f425f3ec95c31..ccb97d7df4c3b 100644 --- a/src/librustdoc/html/static/css/noscript.css +++ b/src/librustdoc/html/static/css/noscript.css @@ -34,7 +34,7 @@ nav.sub { in rustdoc.css */ /* Begin theme: light */ -:root { +:root, :root:not([data-theme]) { --main-background-color: white; --main-color: black; --settings-input-color: #2196f3; @@ -140,7 +140,7 @@ nav.sub { @media (prefers-color-scheme: dark) { /* Begin theme: dark */ - :root { + :root, :root:not([data-theme]) { --main-background-color: #353535; --main-color: #ddd; --settings-input-color: #2196f3; diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 9993dfb1d8c20..0bb073b1ceac0 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -2315,8 +2315,14 @@ in src-script.js and main.js tooling to ensure different themes all define all the variables. Do not alter their formatting. */ +/* +About `:root:not([data-theme])`: if for any reason the JS is enabled but cannot be loaded, +`noscript` won't be enabled and the doc will have no color applied. To do around this, we +add a selector check that if `data-theme` is not defined, then we apply the light theme +by default. +*/ /* Begin theme: light */ -:root[data-theme="light"] { +:root[data-theme="light"], :root:not([data-theme]) { --main-background-color: white; --main-color: black; --settings-input-color: #2196f3; diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index b9a769a7c6da4..940b62be0c94d 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -329,6 +329,30 @@ function preLoadCss(cssUrl) { search.innerHTML = "

" + searchState.loadingText + "

"; searchState.showResults(search); }, + descShards: new Map(), + loadDesc: async function({descShard, descIndex}) { + if (descShard.promise === null) { + descShard.promise = new Promise((resolve, reject) => { + // The `resolve` callback is stored in the `descShard` + // object, which is itself stored in `this.descShards` map. + // It is called in `loadedDescShard` by the + // search.desc script. + descShard.resolve = resolve; + const ds = descShard; + const fname = `${ds.crate}-desc-${ds.shard}-`; + const url = resourcePath( + `search.desc/${descShard.crate}/${fname}`, + ".js", + ); + loadScript(url, reject); + }); + } + const list = await descShard.promise; + return list[descIndex]; + }, + loadedDescShard: function(crate, shard, data) { + this.descShards.get(crate)[shard].resolve(data.split("\n")); + }, }; const toggleAllDocsId = "toggle-all-docs"; @@ -381,7 +405,7 @@ function preLoadCss(cssUrl) { window.location.replace("#" + item.id); }, 0); } - } + }, ); } } @@ -585,7 +609,7 @@ function preLoadCss(cssUrl) { const script = document .querySelector("script[data-ignore-extern-crates]"); const ignoreExternCrates = new Set( - (script ? script.getAttribute("data-ignore-extern-crates") : "").split(",") + (script ? script.getAttribute("data-ignore-extern-crates") : "").split(","), ); for (const lib of libs) { if (lib === window.currentCrate || ignoreExternCrates.has(lib)) { @@ -1098,7 +1122,7 @@ function preLoadCss(cssUrl) { } else { wrapper.style.setProperty( "--popover-arrow-offset", - (wrapperPos.right - pos.right + 4) + "px" + (wrapperPos.right - pos.right + 4) + "px", ); } wrapper.style.visibility = ""; @@ -1680,7 +1704,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm pendingSidebarResizingFrame = false; document.documentElement.style.setProperty( "--resizing-sidebar-width", - desiredSidebarSize + "px" + desiredSidebarSize + "px", ); }, 100); } diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 875ebe2fc90d4..3daf1ad22ded3 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -206,14 +206,14 @@ const editDistanceState = { // insertion this.current[j - 1] + 1, // substitution - this.prev[j - 1] + substitutionCost + this.prev[j - 1] + substitutionCost, ); if ((i > 1) && (j > 1) && (a[aIdx] === b[bIdx - 1]) && (a[aIdx - 1] === b[bIdx])) { // transposition this.current[j] = Math.min( this.current[j], - this.prevPrev[j - 2] + 1 + this.prevPrev[j - 2] + 1, ); } } @@ -242,6 +242,14 @@ function initSearch(rawSearchIndex) { * @type {Array} */ let searchIndex; + /** + * @type {Map} + */ + let searchIndexDeprecated; + /** + * @type {Map} + */ + let searchIndexEmptyDesc; /** * @type {Uint32Array} */ @@ -426,7 +434,7 @@ function initSearch(rawSearchIndex) { return c === "," || c === "="; } -/** + /** * Returns `true` if the given `c` character is a path separator. For example * `:` in `a::b` or a whitespace in `a b`. * @@ -856,8 +864,8 @@ function initSearch(rawSearchIndex) { parserState, parserState.userQuery.slice(start, end), generics, - isInGenerics - ) + isInGenerics, + ), ); } } @@ -1295,7 +1303,7 @@ function initSearch(rawSearchIndex) { * * @return {ResultsTable} */ - function execQuery(parsedQuery, filterCrates, currentCrate) { + async function execQuery(parsedQuery, filterCrates, currentCrate) { const results_others = new Map(), results_in_args = new Map(), results_returned = new Map(); @@ -1342,9 +1350,9 @@ function initSearch(rawSearchIndex) { * @param {Results} results * @param {boolean} isType * @param {string} preferredCrate - * @returns {[ResultObject]} + * @returns {Promise<[ResultObject]>} */ - function sortResults(results, isType, preferredCrate) { + async function sortResults(results, isType, preferredCrate) { const userQuery = parsedQuery.userQuery; const result_list = []; for (const result of results.values()) { @@ -1394,8 +1402,8 @@ function initSearch(rawSearchIndex) { } // sort deprecated items later - a = aaa.item.deprecated; - b = bbb.item.deprecated; + a = searchIndexDeprecated.get(aaa.item.crate).contains(aaa.item.bitIndex); + b = searchIndexDeprecated.get(bbb.item.crate).contains(bbb.item.bitIndex); if (a !== b) { return a - b; } @@ -1422,8 +1430,8 @@ function initSearch(rawSearchIndex) { } // sort by description (no description goes later) - a = (aaa.item.desc === ""); - b = (bbb.item.desc === ""); + a = searchIndexEmptyDesc.get(aaa.item.crate).contains(aaa.item.bitIndex); + b = searchIndexEmptyDesc.get(bbb.item.crate).contains(bbb.item.bitIndex); if (a !== b) { return a - b; } @@ -1446,7 +1454,16 @@ function initSearch(rawSearchIndex) { return 0; }); - return transformResults(result_list); + const transformed = transformResults(result_list); + const descs = await Promise.all(transformed.map(result => { + return searchIndexEmptyDesc.get(result.crate).contains(result.bitIndex) ? + "" : + searchState.loadDesc(result); + })); + for (const [i, result] of transformed.entries()) { + result.desc = descs[i]; + } + return transformed; } /** @@ -1477,7 +1494,7 @@ function initSearch(rawSearchIndex) { whereClause, mgensIn, solutionCb, - unboxingDepth + unboxingDepth, ) { if (unboxingDepth >= UNBOXING_LIMIT) { return false; @@ -1524,7 +1541,7 @@ function initSearch(rawSearchIndex) { queryElem, whereClause, mgens, - unboxingDepth + 1 + unboxingDepth + 1, )) { continue; } @@ -1541,7 +1558,7 @@ function initSearch(rawSearchIndex) { whereClause, mgensScratch, solutionCb, - unboxingDepth + 1 + unboxingDepth + 1, )) { return true; } @@ -1551,7 +1568,7 @@ function initSearch(rawSearchIndex) { whereClause, mgens ? new Map(mgens) : null, solutionCb, - unboxingDepth + 1 + unboxingDepth + 1, )) { return true; } @@ -1625,7 +1642,7 @@ function initSearch(rawSearchIndex) { queryElem, whereClause, mgensScratch, - unboxingDepth + unboxingDepth, ); if (!solution) { return false; @@ -1638,7 +1655,7 @@ function initSearch(rawSearchIndex) { whereClause, simplifiedMgens, solutionCb, - unboxingDepth + unboxingDepth, ); if (passesUnification) { return true; @@ -1646,7 +1663,7 @@ function initSearch(rawSearchIndex) { } return false; }, - unboxingDepth + unboxingDepth, ); if (passesUnification) { return true; @@ -1663,7 +1680,7 @@ function initSearch(rawSearchIndex) { queryElem, whereClause, mgens, - unboxingDepth + 1 + unboxingDepth + 1, )) { continue; } @@ -1689,7 +1706,7 @@ function initSearch(rawSearchIndex) { whereClause, mgensScratch, solutionCb, - unboxingDepth + 1 + unboxingDepth + 1, ); if (passesUnification) { return true; @@ -1820,7 +1837,7 @@ function initSearch(rawSearchIndex) { queryElem, whereClause, mgensIn, - unboxingDepth + unboxingDepth, ) { if (fnType.bindings.size < queryElem.bindings.size) { return false; @@ -1849,7 +1866,7 @@ function initSearch(rawSearchIndex) { // possible solutions return false; }, - unboxingDepth + unboxingDepth, ); return newSolutions; }); @@ -1887,7 +1904,7 @@ function initSearch(rawSearchIndex) { queryElem, whereClause, mgens, - unboxingDepth + unboxingDepth, ) { if (unboxingDepth >= UNBOXING_LIMIT) { return false; @@ -1914,7 +1931,7 @@ function initSearch(rawSearchIndex) { queryElem, whereClause, mgensTmp, - unboxingDepth + unboxingDepth, ); } else if (fnType.generics.length > 0 || fnType.bindings.size > 0) { const simplifiedGenerics = [ @@ -1926,7 +1943,7 @@ function initSearch(rawSearchIndex) { queryElem, whereClause, mgens, - unboxingDepth + unboxingDepth, ); } return false; @@ -1975,7 +1992,7 @@ function initSearch(rawSearchIndex) { elem, whereClause, mgens, - unboxingDepth + 1 + unboxingDepth + 1, ); } if (row.id > 0 && elem.id > 0 && elem.pathWithoutLast.length === 0 && @@ -1989,7 +2006,7 @@ function initSearch(rawSearchIndex) { elem, whereClause, mgens, - unboxingDepth + unboxingDepth, ); } } @@ -2007,7 +2024,7 @@ function initSearch(rawSearchIndex) { return 0; } const maxPathEditDistance = Math.floor( - contains.reduce((acc, next) => acc + next.length, 0) / 3 + contains.reduce((acc, next) => acc + next.length, 0) / 3, ); let ret_dist = maxPathEditDistance + 1; const path = ty.path.split("::"); @@ -2066,12 +2083,13 @@ function initSearch(rawSearchIndex) { crate: item.crate, name: item.name, path: item.path, - desc: item.desc, + descShard: item.descShard, + descIndex: item.descIndex, ty: item.ty, parent: item.parent, type: item.type, is_alias: true, - deprecated: item.deprecated, + bitIndex: item.bitIndex, implDisambiguator: item.implDisambiguator, }; } @@ -2192,7 +2210,7 @@ function initSearch(rawSearchIndex) { results_others, results_in_args, results_returned, - maxEditDistance + maxEditDistance, ) { if (!row || (filterCrates !== null && row.crate !== filterCrates)) { return; @@ -2204,7 +2222,7 @@ function initSearch(rawSearchIndex) { // atoms in the function not present in the query const tfpDist = compareTypeFingerprints( fullId, - parsedQuery.typeFingerprint + parsedQuery.typeFingerprint, ); if (tfpDist !== null) { const in_args = row.type && row.type.inputs @@ -2276,7 +2294,7 @@ function initSearch(rawSearchIndex) { const tfpDist = compareTypeFingerprints( row.id, - parsedQuery.typeFingerprint + parsedQuery.typeFingerprint, ); if (tfpDist === null) { return; @@ -2298,10 +2316,10 @@ function initSearch(rawSearchIndex) { row.type.where_clause, mgens, null, - 0 // unboxing depth + 0, // unboxing depth ); }, - 0 // unboxing depth + 0, // unboxing depth )) { return; } @@ -2419,7 +2437,7 @@ function initSearch(rawSearchIndex) { } return [typeNameIdMap.get(name).id, constraints]; - }) + }), ); } @@ -2446,7 +2464,7 @@ function initSearch(rawSearchIndex) { results_others, results_in_args, results_returned, - maxEditDistance + maxEditDistance, ); } } @@ -2477,10 +2495,15 @@ function initSearch(rawSearchIndex) { innerRunQuery(); } - const ret = createQueryResults( + const [sorted_in_args, sorted_returned, sorted_others] = await Promise.all([ sortResults(results_in_args, true, currentCrate), sortResults(results_returned, true, currentCrate), sortResults(results_others, false, currentCrate), + ]); + const ret = createQueryResults( + sorted_in_args, + sorted_returned, + sorted_others, parsedQuery); handleAliases(ret, parsedQuery.original.replace(/"/g, ""), filterCrates, currentCrate); if (parsedQuery.error !== null && ret.others.length !== 0) { @@ -2581,14 +2604,14 @@ function initSearch(rawSearchIndex) { * @param {ParsedQuery} query * @param {boolean} display - True if this is the active tab */ - function addTab(array, query, display) { + async function addTab(array, query, display) { const extraClass = display ? " active" : ""; const output = document.createElement("div"); if (array.length > 0) { output.className = "search-results " + extraClass; - array.forEach(item => { + for (const item of array) { const name = item.name; const type = itemTypes[item.ty]; const longType = longItemTypes[item.ty]; @@ -2624,7 +2647,7 @@ ${item.displayPath}${name}\ link.appendChild(description); output.appendChild(link); - }); + } } else if (query.error === null) { output.className = "search-failed" + extraClass; output.innerHTML = "No results :(
" + @@ -2666,7 +2689,7 @@ ${item.displayPath}${name}\ * @param {boolean} go_to_first * @param {string} filterCrates */ - function showResults(results, go_to_first, filterCrates) { + async function showResults(results, go_to_first, filterCrates) { const search = searchState.outputElement(); if (go_to_first || (results.others.length === 1 && getSettingValue("go-to-only-result") === "true") @@ -2699,9 +2722,11 @@ ${item.displayPath}${name}\ currentResults = results.query.userQuery; - const ret_others = addTab(results.others, results.query, true); - const ret_in_args = addTab(results.in_args, results.query, false); - const ret_returned = addTab(results.returned, results.query, false); + const [ret_others, ret_in_args, ret_returned] = await Promise.all([ + addTab(results.others, results.query, true), + addTab(results.in_args, results.query, false), + addTab(results.returned, results.query, false), + ]); // Navigate to the relevant tab if the current tab is empty, like in case users search // for "-> String". If they had selected another tab previously, they have to click on @@ -2822,7 +2847,7 @@ ${item.displayPath}${name}\ * and display the results. * @param {boolean} [forced] */ - function search(forced) { + async function search(forced) { const query = parseQuery(searchState.input.value.trim()); let filterCrates = getFilterCrates(); @@ -2850,8 +2875,8 @@ ${item.displayPath}${name}\ // recent search query is added to the browser history. updateSearchHistory(buildUrl(query.original, filterCrates)); - showResults( - execQuery(query, filterCrates, window.currentCrate), + await showResults( + await execQuery(query, filterCrates, window.currentCrate), params.go_to_first, filterCrates); } @@ -2920,7 +2945,7 @@ ${item.displayPath}${name}\ pathIndex = type[PATH_INDEX_DATA]; generics = buildItemSearchTypeAll( type[GENERICS_DATA], - lowercasePaths + lowercasePaths, ); if (type.length > BINDINGS_DATA && type[BINDINGS_DATA].length > 0) { bindings = new Map(type[BINDINGS_DATA].map(binding => { @@ -3030,101 +3055,49 @@ ${item.displayPath}${name}\ * The raw function search type format is generated using serde in * librustdoc/html/render/mod.rs: IndexItemFunctionType::write_to_string * - * @param {{ - * string: string, - * offset: number, - * backrefQueue: FunctionSearchType[] - * }} itemFunctionDecoder * @param {Array<{name: string, ty: number}>} lowercasePaths - * @param {Map} * * @return {null|FunctionSearchType} */ - function buildFunctionSearchType(itemFunctionDecoder, lowercasePaths) { - const c = itemFunctionDecoder.string.charCodeAt(itemFunctionDecoder.offset); - itemFunctionDecoder.offset += 1; - const [zero, ua, la, ob, cb] = ["0", "@", "`", "{", "}"].map(c => c.charCodeAt(0)); - // `` ` `` is used as a sentinel because it's fewer bytes than `null`, and decodes to zero - // `0` is a backref - if (c === la) { - return null; - } - // sixteen characters after "0" are backref - if (c >= zero && c < ua) { - return itemFunctionDecoder.backrefQueue[c - zero]; - } - if (c !== ob) { - throw ["Unexpected ", c, " in function: expected ", "{", "; this is a bug"]; - } - // call after consuming `{` - function decodeList() { - let c = itemFunctionDecoder.string.charCodeAt(itemFunctionDecoder.offset); - const ret = []; - while (c !== cb) { - ret.push(decode()); - c = itemFunctionDecoder.string.charCodeAt(itemFunctionDecoder.offset); - } - itemFunctionDecoder.offset += 1; // eat cb - return ret; - } - // consumes and returns a list or integer - function decode() { - let n = 0; - let c = itemFunctionDecoder.string.charCodeAt(itemFunctionDecoder.offset); - if (c === ob) { - itemFunctionDecoder.offset += 1; - return decodeList(); - } - while (c < la) { - n = (n << 4) | (c & 0xF); - itemFunctionDecoder.offset += 1; - c = itemFunctionDecoder.string.charCodeAt(itemFunctionDecoder.offset); - } - // last character >= la - n = (n << 4) | (c & 0xF); - const [sign, value] = [n & 1, n >> 1]; - itemFunctionDecoder.offset += 1; - return sign ? -value : value; - } - const functionSearchType = decodeList(); - const INPUTS_DATA = 0; - const OUTPUT_DATA = 1; - let inputs, output; - if (typeof functionSearchType[INPUTS_DATA] === "number") { - inputs = [buildItemSearchType(functionSearchType[INPUTS_DATA], lowercasePaths)]; - } else { - inputs = buildItemSearchTypeAll( - functionSearchType[INPUTS_DATA], - lowercasePaths - ); - } - if (functionSearchType.length > 1) { - if (typeof functionSearchType[OUTPUT_DATA] === "number") { - output = [buildItemSearchType(functionSearchType[OUTPUT_DATA], lowercasePaths)]; + function buildFunctionSearchTypeCallback(lowercasePaths) { + return functionSearchType => { + if (functionSearchType === 0) { + return null; + } + const INPUTS_DATA = 0; + const OUTPUT_DATA = 1; + let inputs, output; + if (typeof functionSearchType[INPUTS_DATA] === "number") { + inputs = [buildItemSearchType(functionSearchType[INPUTS_DATA], lowercasePaths)]; } else { - output = buildItemSearchTypeAll( - functionSearchType[OUTPUT_DATA], - lowercasePaths + inputs = buildItemSearchTypeAll( + functionSearchType[INPUTS_DATA], + lowercasePaths, ); } - } else { - output = []; - } - const where_clause = []; - const l = functionSearchType.length; - for (let i = 2; i < l; ++i) { - where_clause.push(typeof functionSearchType[i] === "number" - ? [buildItemSearchType(functionSearchType[i], lowercasePaths)] - : buildItemSearchTypeAll(functionSearchType[i], lowercasePaths)); - } - const ret = { - inputs, output, where_clause, + if (functionSearchType.length > 1) { + if (typeof functionSearchType[OUTPUT_DATA] === "number") { + output = [buildItemSearchType(functionSearchType[OUTPUT_DATA], lowercasePaths)]; + } else { + output = buildItemSearchTypeAll( + functionSearchType[OUTPUT_DATA], + lowercasePaths, + ); + } + } else { + output = []; + } + const where_clause = []; + const l = functionSearchType.length; + for (let i = 2; i < l; ++i) { + where_clause.push(typeof functionSearchType[i] === "number" + ? [buildItemSearchType(functionSearchType[i], lowercasePaths)] + : buildItemSearchTypeAll(functionSearchType[i], lowercasePaths)); + } + return { + inputs, output, where_clause, + }; }; - itemFunctionDecoder.backrefQueue.unshift(ret); - if (itemFunctionDecoder.backrefQueue.length > 16) { - itemFunctionDecoder.backrefQueue.pop(); - } - return ret; } /** @@ -3245,6 +3218,185 @@ ${item.displayPath}${name}\ return functionTypeFingerprint[(fullId * 4) + 3]; } + class VlqHexDecoder { + constructor(string, cons) { + this.string = string; + this.cons = cons; + this.offset = 0; + this.backrefQueue = []; + } + // call after consuming `{` + decodeList() { + const cb = "}".charCodeAt(0); + let c = this.string.charCodeAt(this.offset); + const ret = []; + while (c !== cb) { + ret.push(this.decode()); + c = this.string.charCodeAt(this.offset); + } + this.offset += 1; // eat cb + return ret; + } + // consumes and returns a list or integer + decode() { + const [ob, la] = ["{", "`"].map(c => c.charCodeAt(0)); + let n = 0; + let c = this.string.charCodeAt(this.offset); + if (c === ob) { + this.offset += 1; + return this.decodeList(); + } + while (c < la) { + n = (n << 4) | (c & 0xF); + this.offset += 1; + c = this.string.charCodeAt(this.offset); + } + // last character >= la + n = (n << 4) | (c & 0xF); + const [sign, value] = [n & 1, n >> 1]; + this.offset += 1; + return sign ? -value : value; + } + next() { + const c = this.string.charCodeAt(this.offset); + const [zero, ua, la] = ["0", "@", "`"].map(c => c.charCodeAt(0)); + // sixteen characters after "0" are backref + if (c >= zero && c < ua) { + this.offset += 1; + return this.backrefQueue[c - zero]; + } + // special exception: 0 doesn't use backref encoding + // it's already one character, and it's always nullish + if (c === la) { + this.offset += 1; + return this.cons(0); + } + const result = this.cons(this.decode()); + this.backrefQueue.unshift(result); + if (this.backrefQueue.length > 16) { + this.backrefQueue.pop(); + } + return result; + } + } + class RoaringBitmap { + constructor(str) { + const strdecoded = atob(str); + const u8array = new Uint8Array(strdecoded.length); + for (let j = 0; j < strdecoded.length; ++j) { + u8array[j] = strdecoded.charCodeAt(j); + } + const has_runs = u8array[0] === 0x3b; + const size = has_runs ? + ((u8array[2] | (u8array[3] << 8)) + 1) : + ((u8array[4] | (u8array[5] << 8) | (u8array[6] << 16) | (u8array[7] << 24))); + let i = has_runs ? 4 : 8; + let is_run; + if (has_runs) { + const is_run_len = Math.floor((size + 7) / 8); + is_run = u8array.slice(i, i + is_run_len); + i += is_run_len; + } else { + is_run = new Uint8Array(); + } + this.keys = []; + this.cardinalities = []; + for (let j = 0; j < size; ++j) { + this.keys.push(u8array[i] | (u8array[i + 1] << 8)); + i += 2; + this.cardinalities.push((u8array[i] | (u8array[i + 1] << 8)) + 1); + i += 2; + } + this.containers = []; + let offsets = null; + if (!has_runs || this.keys.length >= 4) { + offsets = []; + for (let j = 0; j < size; ++j) { + offsets.push(u8array[i] | (u8array[i + 1] << 8) | (u8array[i + 2] << 16) | + (u8array[i + 3] << 24)); + i += 4; + } + } + for (let j = 0; j < size; ++j) { + if (offsets && offsets[j] !== i) { + console.log(this.containers); + throw new Error(`corrupt bitmap ${j}: ${i} / ${offsets[j]}`); + } + if (is_run[j >> 3] & (1 << (j & 0x7))) { + const runcount = (u8array[i] | (u8array[i + 1] << 8)); + i += 2; + this.containers.push(new RoaringBitmapRun( + runcount, + u8array.slice(i, i + (runcount * 4)), + )); + i += runcount * 4; + } else if (this.cardinalities[j] >= 4096) { + this.containers.push(new RoaringBitmapBits(u8array.slice(i, i + 8192))); + i += 8192; + } else { + const end = this.cardinalities[j] * 2; + this.containers.push(new RoaringBitmapArray( + this.cardinalities[j], + u8array.slice(i, i + end), + )); + i += end; + } + } + } + contains(keyvalue) { + const key = keyvalue >> 16; + const value = keyvalue & 0xFFFF; + for (let i = 0; i < this.keys.length; ++i) { + if (this.keys[i] === key) { + return this.containers[i].contains(value); + } + } + return false; + } + } + + class RoaringBitmapRun { + constructor(runcount, array) { + this.runcount = runcount; + this.array = array; + } + contains(value) { + const l = this.runcount * 4; + for (let i = 0; i < l; i += 4) { + const start = this.array[i] | (this.array[i + 1] << 8); + const lenm1 = this.array[i + 2] | (this.array[i + 3] << 8); + if (value >= start && value <= (start + lenm1)) { + return true; + } + } + return false; + } + } + class RoaringBitmapArray { + constructor(cardinality, array) { + this.cardinality = cardinality; + this.array = array; + } + contains(value) { + const l = this.cardinality * 2; + for (let i = 0; i < l; i += 2) { + const start = this.array[i] | (this.array[i + 1] << 8); + if (value === start) { + return true; + } + } + return false; + } + } + class RoaringBitmapBits { + constructor(array) { + this.array = array; + } + contains(value) { + return !!(this.array[value >> 3] & (1 << (value & 7))); + } + } + /** * Convert raw search index into in-memory search index. * @@ -3252,6 +3404,8 @@ ${item.displayPath}${name}\ */ function buildIndex(rawSearchIndex) { searchIndex = []; + searchIndexDeprecated = new Map(); + searchIndexEmptyDesc = new Map(); const charA = "A".charCodeAt(0); let currentIndex = 0; let id = 0; @@ -3271,26 +3425,48 @@ ${item.displayPath}${name}\ id = 0; for (const [crate, crateCorpus] of rawSearchIndex) { + // a string representing the lengths of each description shard + // a string representing the list of function types + const itemDescShardDecoder = new VlqHexDecoder(crateCorpus.D, noop => noop); + let descShard = { + crate, + shard: 0, + start: 0, + len: itemDescShardDecoder.next(), + promise: null, + resolve: null, + }; + const descShardList = [ descShard ]; + + // Deprecated items and items with no description + searchIndexDeprecated.set(crate, new RoaringBitmap(crateCorpus.c)); + searchIndexEmptyDesc.set(crate, new RoaringBitmap(crateCorpus.e)); + let descIndex = 0; + // This object should have exactly the same set of fields as the "row" // object defined below. Your JavaScript runtime will thank you. // https://mathiasbynens.be/notes/shapes-ics const crateRow = { - crate: crate, + crate, ty: 3, // == ExternCrate name: crate, path: "", - desc: crateCorpus.doc, + descShard, + descIndex, parent: undefined, type: null, - id: id, + id, word: crate, normalizedName: crate.indexOf("_") === -1 ? crate : crate.replace(/_/g, ""), - deprecated: null, + bitIndex: 0, implDisambiguator: null, }; id += 1; searchIndex.push(crateRow); currentIndex += 1; + if (!searchIndexEmptyDesc.get(crate).contains(0)) { + descIndex += 1; + } // a String of one character item type codes const itemTypes = crateCorpus.t; @@ -3302,19 +3478,9 @@ ${item.displayPath}${name}\ // i.e. if indices 4 and 11 are present, but 5-10 and 12-13 are not present, // 5-10 will fall back to the path for 4 and 12-13 will fall back to the path for 11 const itemPaths = new Map(crateCorpus.q); - // an array of (String) descriptions - const itemDescs = crateCorpus.d; // an array of (Number) the parent path index + 1 to `paths`, or 0 if none const itemParentIdxs = crateCorpus.i; - // a string representing the list of function types - const itemFunctionDecoder = { - string: crateCorpus.f, - offset: 0, - backrefQueue: [], - }; - // an array of (Number) indices for the deprecated items - const deprecatedItems = new Set(crateCorpus.c); - // an array of (Number) indices for the deprecated items + // a map Number, string for impl disambiguators const implDisambiguator = new Map(crateCorpus.b); // an array of [(Number) item type, // (String) name] @@ -3326,6 +3492,12 @@ ${item.displayPath}${name}\ // an array of [{name: String, ty: Number}] const lowercasePaths = []; + // a string representing the list of function types + const itemFunctionDecoder = new VlqHexDecoder( + crateCorpus.f, + buildFunctionSearchTypeCallback(lowercasePaths), + ); + // convert `rawPaths` entries into object form // generate normalizedPaths for function search mode let len = paths.length; @@ -3354,12 +3526,26 @@ ${item.displayPath}${name}\ lastPath = ""; len = itemTypes.length; for (let i = 0; i < len; ++i) { + const bitIndex = i + 1; + if (descIndex >= descShard.len && + !searchIndexEmptyDesc.get(crate).contains(bitIndex)) { + descShard = { + crate, + shard: descShard.shard + 1, + start: descShard.start + descShard.len, + len: itemDescShardDecoder.next(), + promise: null, + resolve: null, + }; + descIndex = 0; + descShardList.push(descShard); + } let word = ""; if (typeof itemNames[i] === "string") { word = itemNames[i].toLowerCase(); } const path = itemPaths.has(i) ? itemPaths.get(i) : lastPath; - const type = buildFunctionSearchType(itemFunctionDecoder, lowercasePaths); + const type = itemFunctionDecoder.next(); if (type !== null) { if (type) { const fp = functionTypeFingerprint.subarray(id * 4, (id + 1) * 4); @@ -3380,22 +3566,26 @@ ${item.displayPath}${name}\ // This object should have exactly the same set of fields as the "crateRow" // object defined above. const row = { - crate: crate, + crate, ty: itemTypes.charCodeAt(i) - charA, name: itemNames[i], - path: path, - desc: itemDescs[i], + path, + descShard, + descIndex, parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined, type, - id: id, + id, word, normalizedName: word.indexOf("_") === -1 ? word : word.replace(/_/g, ""), - deprecated: deprecatedItems.has(i), + bitIndex, implDisambiguator: implDisambiguator.has(i) ? implDisambiguator.get(i) : null, }; id += 1; searchIndex.push(row); lastPath = row.path; + if (!searchIndexEmptyDesc.get(crate).contains(bitIndex)) { + descIndex += 1; + } } if (aliases) { @@ -3419,6 +3609,7 @@ ${item.displayPath}${name}\ } } currentIndex += itemTypes.length; + searchState.descShards.set(crate, descShardList); } // Drop the (rather large) hash table used for reusing function items TYPES_POOL = new Map(); diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index bda7b3c647e7e..73c543567c078 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -211,14 +211,14 @@ function updateSidebarWidth() { if (desktopSidebarWidth && desktopSidebarWidth !== "null") { document.documentElement.style.setProperty( "--desktop-sidebar-width", - desktopSidebarWidth + "px" + desktopSidebarWidth + "px", ); } const srcSidebarWidth = getSettingValue("src-sidebar-width"); if (srcSidebarWidth && srcSidebarWidth !== "null") { document.documentElement.style.setProperty( "--src-sidebar-width", - srcSidebarWidth + "px" + srcSidebarWidth + "px", ); } } diff --git a/src/tools/cargo b/src/tools/cargo index 499a61ce7a0fc..0637083df5bbd 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 499a61ce7a0fc6a72040084862a68b2603e770e8 +Subproject commit 0637083df5bbdcc951845f0d2eff6999cdb6d30a diff --git a/src/tools/clippy/.gitignore b/src/tools/clippy/.gitignore index 503ae3c509039..181b71a658b9a 100644 --- a/src/tools/clippy/.gitignore +++ b/src/tools/clippy/.gitignore @@ -1,3 +1,6 @@ +# Generated by ui-test +rustc-ice-* + # Used by CI to be able to push: /.github/deploy_key out diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 76ef84a48b817..f7e7ed86eed8c 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -5379,6 +5379,7 @@ Released 2018-09-13 [`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays [`large_stack_frames`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_frames [`large_types_passed_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value +[`legacy_numeric_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants [`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty [`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero [`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return @@ -5481,6 +5482,7 @@ Released 2018-09-13 [`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc [`missing_spin_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_spin_loop [`missing_trait_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_trait_methods +[`missing_transmute_annotations`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_transmute_annotations [`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes [`mixed_attributes_style`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_attributes_style [`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index 2b37b54c0048b..43f20ecedc21d 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -31,7 +31,6 @@ anstream = "0.6.0" [dev-dependencies] ui_test = "0.22.2" -tester = "0.9" regex = "1.5.5" toml = "0.7.3" walkdir = "2.3" diff --git a/src/tools/clippy/book/src/SUMMARY.md b/src/tools/clippy/book/src/SUMMARY.md index a048fbbd8acf9..be13fcbe260fa 100644 --- a/src/tools/clippy/book/src/SUMMARY.md +++ b/src/tools/clippy/book/src/SUMMARY.md @@ -32,3 +32,4 @@ - [Proposals](development/proposals/README.md) - [Roadmap 2021](development/proposals/roadmap-2021.md) - [Syntax Tree Patterns](development/proposals/syntax-tree-patterns.md) + - [The Team](development/the_team.md) diff --git a/src/tools/clippy/book/src/development/defining_lints.md b/src/tools/clippy/book/src/development/defining_lints.md index 54f77b00190be..806ed0845f031 100644 --- a/src/tools/clippy/book/src/development/defining_lints.md +++ b/src/tools/clippy/book/src/development/defining_lints.md @@ -62,9 +62,8 @@ $ cargo dev new_lint --name=lint_name --pass=late --category=pedantic There are two things to note here: 1. `--pass`: We set `--pass=late` in this command to do a late lint pass. The - alternative is an `early` lint pass. We will discuss this difference in a - later chapter. - + alternative is an `early` lint pass. We will discuss this difference in the + [Lint Passes] chapter. 2. `--category`: If not provided, the `category` of this new lint will default to `nursery`. @@ -194,8 +193,7 @@ store.register_late_pass(|_| Box::new(foo_functions::FooFunctions)); As you might have guessed, where there's something late, there is something early: in Clippy there is a `register_early_pass` method as well. More on early -vs. late passes in a later chapter. - +vs. late passes in the [Lint Passes] chapter. Without a call to one of `register_early_pass` or `register_late_pass`, the lint pass in question will not be run. @@ -203,3 +201,4 @@ pass in question will not be run. [all_lints]: https://rust-lang.github.io/rust-clippy/master/ [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints +[Lint Passes]: lint_passes.md diff --git a/src/tools/clippy/book/src/development/lint_passes.md b/src/tools/clippy/book/src/development/lint_passes.md index 621fc20972ea1..dde9e1a273bcf 100644 --- a/src/tools/clippy/book/src/development/lint_passes.md +++ b/src/tools/clippy/book/src/development/lint_passes.md @@ -50,7 +50,7 @@ questions already, but the parser is okay with it. This is what we mean when we say `EarlyLintPass` deals with only syntax on the AST level. Alternatively, think of the `foo_functions` lint we mentioned in -define new lints chapter. +the [Define New Lints](defining_lints.md) chapter. We want the `foo_functions` lint to detect functions with `foo` as their name. Writing a lint that only checks for the name of a function means that we only diff --git a/src/tools/clippy/book/src/development/the_team.md b/src/tools/clippy/book/src/development/the_team.md new file mode 100644 index 0000000000000..10341791cec4b --- /dev/null +++ b/src/tools/clippy/book/src/development/the_team.md @@ -0,0 +1,130 @@ +# The team + +Everyone who contributes to Clippy makes the project what it is. Collaboration +and discussions are the lifeblood of every open-source project. Clippy has a +very flat hierarchy. The teams mainly have additional access rights to the repo. + +This document outlines the onboarding process, as well as duties, and access +rights for members of a group. + +All regular events mentioned in this chapter are tracked in the [calendar repository]. +The calendar file is also available for download: [clippy.ics] + +## Everyone + +Everyone, including you, is welcome to join discussions and contribute in other +ways, like PRs. + +You also have some triage rights, using `@rustbot` to add labels and claim +issues. See [labeling with @rustbot]. + +A rule for everyone should be to keep a healthy work-life balance. Take a break +when you need one. + +## Clippy-Contributors + +This is a group of regular contributors to Clippy to help with triaging. + +### Duties + +This team exists to make contributing easier for regular members. It doesn't +carry any duties that need to be done. However, we want to encourage members of +this group to help with triaging, which can include: + +1. **Labeling issues** + + For the `good-first-issue` label, it can still be good to use `@rustbot` to + subscribe to the issue and help interested parties, if they post questions + in the comments. + +2. **Closing duplicate or resolved issues** + + When you manually close an issue, it's often a good idea, to add a short + comment explaining the reason. + +3. **Ping people after two weeks of inactivity** + + We try to keep issue assignments and PRs fairly up-to-date. After two weeks, + it can be good to send a friendly ping to the delaying party. + + You might close a PR with the `I-inactive-closed` label if the author is + busy or wants to abandon it. If the reviewer is busy, the PR can be + reassigned to someone else. + + Checkout: https://triage.rust-lang.org/triage/rust-lang/rust-clippy to + monitor PRs. + +While not part of their duties, contributors are encouraged to review PRs +and help on Zulip. The team always appreciates help! + +### Membership + +If you have been contributing to Clippy for some time, we'll probably ask you if +you want to join this team. Members of this team are also welcome to suggest +people who they think would make a great addition to this group. + +For this group, there is no direct onboarding process. You're welcome to just +continue what you've been doing. If you like, you can ask for someone to mentor +you, either in the Clippy stream on Zulip or privately via a PM. + +If you have been inactive in Clippy for over three months, we'll probably move +you to the alumni group. You're always welcome to come back. + +## The Clippy Team + +[The Clippy team](https://www.rust-lang.org/governance/teams/dev-tools#Clippy%20team) +is responsible for maintaining Clippy. + +### Duties + +1. **Respond to PRs in a timely manner** + + It's totally fine, if you don't have the time for reviews right now. + You can reassign the PR to a random member by commenting `r? clippy`. + +2. **Take a break when you need one** + + You are valuable! Clippy wouldn't be what it is without you. So take a break + early and recharge some energy when you need to. + +3. **Be responsive on Zulip** + + This means in a reasonable time frame, so responding within one or two days + is totally fine. + + It's also good, if you answer threads on Zulip and take part in our Clippy + meetings, every two weeks. The meeting dates are tracked in the [calendar repository]. + + +4. **Sync Clippy with the rust-lang/rust repo** + + This is done every two weeks, usually by @flip1995. + +5. **Update the changelog** + + This needs to be done for every release, every six weeks. This is usually + done by @xFrednet. + +### Membership + +If you have been active for some time, we'll probably reach out and ask +if you want to help with reviews and eventually join the Clippy team. + +During the onboarding process, you'll be assigned pull requests to review. +You'll also have an active team member as a mentor who'll stay in contact via +Zulip DMs to provide advice and feedback. If you have questions, you're always +welcome to ask, that is the best way to learn. Once you're done with the review, +you can ping your mentor for a full review and to r+ the PR in both of your names. + +When your mentor is confident that you can handle reviews on your own, they'll +start an informal vote among the active team members to officially add you to +the team. This vote is usually accepted unanimously. Then you'll be added to +the team once you've confirmed that you're still interested in joining. The +onboarding phase typically takes a couple of weeks to a few months. + +If you have been inactive in Clippy for over three months, we'll probably move +you to the alumni group. You're always welcome to come back. + +[calendar repository]: https://github.com/rust-lang/calendar/blob/main/clippy.toml +[clippy.ics]: https://rust-lang.github.io/calendar/clippy.ics +[labeling with @rustbot]: https://forge.rust-lang.org/triagebot/labeling.html diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index a923489974646..4a2727c5197f6 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -616,6 +616,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`if_then_some_else_none`](https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none) * [`index_refutable_slice`](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice) * [`iter_kv_map`](https://rust-lang.github.io/rust-clippy/master/index.html#iter_kv_map) +* [`legacy_numeric_constants`](https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants) * [`manual_bits`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits) * [`manual_c_str_literals`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals) * [`manual_clamp`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp) diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index 3218fe7f45627..53c10f7cee8ba 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -262,7 +262,7 @@ define_Conf! { /// /// Suppress lints whenever the suggested change would cause breakage for other crates. (avoid_breaking_exported_api: bool = true), - /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE, ITER_KV_MAP, MANUAL_C_STR_LITERALS, ASSIGNING_CLONES. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE, ITER_KV_MAP, MANUAL_C_STR_LITERALS, ASSIGNING_CLONES, LEGACY_NUMERIC_CONSTANTS. /// /// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml` #[default_text = ""] @@ -856,11 +856,6 @@ mod tests { } } - assert!( - names.remove("allow-one-hash-in-raw-strings"), - "remove this when #11481 is fixed" - ); - assert!( names.is_empty(), "Configuration variable lacks test: {names:?}\nAdd a test to `tests/ui-toml`" diff --git a/src/tools/clippy/clippy_config/src/lib.rs b/src/tools/clippy/clippy_config/src/lib.rs index 01e2aa6e0a602..ff7fa7241cb96 100644 --- a/src/tools/clippy/clippy_config/src/lib.rs +++ b/src/tools/clippy/clippy_config/src/lib.rs @@ -1,6 +1,12 @@ #![feature(rustc_private, let_chains)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] -#![warn(rust_2018_idioms, unused_lifetimes)] +#![warn( + trivial_casts, + trivial_numeric_casts, + rust_2018_idioms, + unused_lifetimes, + unused_qualifications +)] #![allow( clippy::must_use_candidate, clippy::missing_panics_doc, diff --git a/src/tools/clippy/clippy_config/src/msrvs.rs b/src/tools/clippy/clippy_config/src/msrvs.rs index 149c4776dc9c9..59dd5b334b84b 100644 --- a/src/tools/clippy/clippy_config/src/msrvs.rs +++ b/src/tools/clippy/clippy_config/src/msrvs.rs @@ -36,7 +36,7 @@ msrv_aliases! { 1,47,0 { TAU, IS_ASCII_DIGIT_CONST, ARRAY_IMPL_ANY_LEN } 1,46,0 { CONST_IF_MATCH } 1,45,0 { STR_STRIP_PREFIX } - 1,43,0 { LOG2_10, LOG10_2 } + 1,43,0 { LOG2_10, LOG10_2, NUMERIC_ASSOCIATED_CONSTANTS } 1,42,0 { MATCHES_MACRO, SLICE_PATTERNS, PTR_SLICE_RAW_PARTS } 1,41,0 { RE_REBALANCING_COHERENCE, RESULT_MAP_OR_ELSE } 1,40,0 { MEM_TAKE, NON_EXHAUSTIVE, OPTION_AS_DEREF } diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs index c4ae4f0e2bdd6..bb62e902cd544 100644 --- a/src/tools/clippy/clippy_dev/src/lib.rs +++ b/src/tools/clippy/clippy_dev/src/lib.rs @@ -2,8 +2,13 @@ #![feature(let_chains)] #![feature(rustc_private)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] -// warn on lints, that are included in `rust-lang/rust`s bootstrap -#![warn(rust_2018_idioms, unused_lifetimes)] +#![warn( + trivial_casts, + trivial_numeric_casts, + rust_2018_idioms, + unused_lifetimes, + unused_qualifications +)] // The `rustc_driver` crate seems to be required in order to use the `rust_lexer` crate. #[allow(unused_extern_crates)] diff --git a/src/tools/clippy/clippy_dev/src/lint.rs b/src/tools/clippy/clippy_dev/src/lint.rs index 906a972781092..f308f5dfdfd83 100644 --- a/src/tools/clippy/clippy_dev/src/lint.rs +++ b/src/tools/clippy/clippy_dev/src/lint.rs @@ -20,6 +20,8 @@ pub fn run<'a>(path: &str, args: impl Iterator) { .args(["--edition", "2021"]) .arg(path) .args(args) + // Prevent rustc from creating `rustc-ice-*` files the console output is enough. + .env("RUSTC_ICE", "0") .status(), ); } else { @@ -32,6 +34,8 @@ pub fn run<'a>(path: &str, args: impl Iterator) { let status = Command::new(cargo_clippy_path()) .arg("clippy") .args(args) + // Prevent rustc from creating `rustc-ice-*` files the console output is enough. + .env("RUSTC_ICE", "0") .current_dir(path) .status(); diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs index 76ae26dddf4de..625b133959133 100644 --- a/src/tools/clippy/clippy_dev/src/update_lints.rs +++ b/src/tools/clippy/clippy_dev/src/update_lints.rs @@ -992,7 +992,7 @@ fn replace_region_in_text<'a>( } fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { - match fs::OpenOptions::new().create_new(true).write(true).open(new_name) { + match OpenOptions::new().create_new(true).write(true).open(new_name) { Ok(file) => drop(file), Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false, Err(e) => panic_file(e, new_name, "create"), @@ -1016,7 +1016,7 @@ fn panic_file(error: io::Error, name: &Path, action: &str) -> ! { } fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option) { - let mut file = fs::OpenOptions::new() + let mut file = OpenOptions::new() .write(true) .read(true) .open(path) diff --git a/src/tools/clippy/clippy_lints/src/approx_const.rs b/src/tools/clippy/clippy_lints/src/approx_const.rs index 25606f4253e4a..ec28fd4611184 100644 --- a/src/tools/clippy/clippy_lints/src/approx_const.rs +++ b/src/tools/clippy/clippy_lints/src/approx_const.rs @@ -95,7 +95,7 @@ impl ApproxConstant { cx, APPROX_CONSTANT, e.span, - &format!("approximate value of `{module}::consts::{}` found", &name), + format!("approximate value of `{module}::consts::{}` found", &name), None, "consider using the constant directly", ); diff --git a/src/tools/clippy/clippy_lints/src/asm_syntax.rs b/src/tools/clippy/clippy_lints/src/asm_syntax.rs index c2fa56e136031..7c88bfc97ca49 100644 --- a/src/tools/clippy/clippy_lints/src/asm_syntax.rs +++ b/src/tools/clippy/clippy_lints/src/asm_syntax.rs @@ -53,9 +53,9 @@ fn check_asm_syntax( cx, lint, span, - &format!("{style} x86 assembly syntax used"), + format!("{style} x86 assembly syntax used"), None, - &format!("use {} x86 assembly syntax", !style), + format!("use {} x86 assembly syntax", !style), ); } } diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs index 9365fbfaed088..2003dd1fb0e2c 100644 --- a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs +++ b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs @@ -58,7 +58,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { cx, ASSERTIONS_ON_CONSTANTS, macro_call.span, - &format!( + format!( "`{}!(true)` will be optimized out by the compiler", cx.tcx.item_name(macro_call.def_id) ), @@ -74,9 +74,9 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { cx, ASSERTIONS_ON_CONSTANTS, macro_call.span, - &format!("`assert!(false{assert_arg})` should probably be replaced"), + format!("`assert!(false{assert_arg})` should probably be replaced"), None, - &format!("use `panic!({panic_arg})` or `unreachable!({panic_arg})`"), + format!("use `panic!({panic_arg})` or `unreachable!({panic_arg})`"), ); } } diff --git a/src/tools/clippy/clippy_lints/src/assigning_clones.rs b/src/tools/clippy/clippy_lints/src/assigning_clones.rs index 8e27b3ccefdc3..dc7f44af2b74b 100644 --- a/src/tools/clippy/clippy_lints/src/assigning_clones.rs +++ b/src/tools/clippy/clippy_lints/src/assigning_clones.rs @@ -65,7 +65,7 @@ impl AssigningClones { impl_lint_pass!(AssigningClones => [ASSIGNING_CLONES]); impl<'tcx> LateLintPass<'tcx> for AssigningClones { - fn check_expr(&mut self, cx: &LateContext<'tcx>, assign_expr: &'tcx hir::Expr<'_>) { + fn check_expr(&mut self, cx: &LateContext<'tcx>, assign_expr: &'tcx Expr<'_>) { // Do not fire the lint in macros let expn_data = assign_expr.span().ctxt().outer_expn_data(); match expn_data.kind { @@ -181,6 +181,23 @@ fn is_ok_to_suggest<'tcx>(cx: &LateContext<'tcx>, lhs: &Expr<'tcx>, call: &CallC return false; } + // If the call expression is inside an impl block that contains the method invoked by the + // call expression, we bail out to avoid suggesting something that could result in endless + // recursion. + if let Some(local_block_id) = impl_block.as_local() + && let Some(block) = cx.tcx.hir_node_by_def_id(local_block_id).as_owner() + { + let impl_block_owner = block.def_id(); + if cx + .tcx + .hir() + .parent_id_iter(lhs.hir_id) + .any(|parent| parent.owner == impl_block_owner) + { + return false; + } + } + // Find the function for which we want to check that it is implemented. let provided_fn = match call.target { TargetTrait::Clone => cx.tcx.get_diagnostic_item(sym::Clone).and_then(|clone| { @@ -205,14 +222,9 @@ fn is_ok_to_suggest<'tcx>(cx: &LateContext<'tcx>, lhs: &Expr<'tcx>, call: &CallC implemented_fns.contains_key(&provided_fn.def_id) } -fn suggest<'tcx>( - cx: &LateContext<'tcx>, - assign_expr: &hir::Expr<'tcx>, - lhs: &hir::Expr<'tcx>, - call: &CallCandidate<'tcx>, -) { +fn suggest<'tcx>(cx: &LateContext<'tcx>, assign_expr: &Expr<'tcx>, lhs: &Expr<'tcx>, call: &CallCandidate<'tcx>) { span_lint_and_then(cx, ASSIGNING_CLONES, assign_expr.span, call.message(), |diag| { - let mut applicability = Applicability::MachineApplicable; + let mut applicability = Applicability::Unspecified; diag.span_suggestion( assign_expr.span, @@ -263,7 +275,7 @@ impl<'tcx> CallCandidate<'tcx> { fn suggested_replacement( &self, cx: &LateContext<'tcx>, - lhs: &hir::Expr<'tcx>, + lhs: &Expr<'tcx>, applicability: &mut Applicability, ) -> String { match self.target { diff --git a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs index df00f23e37e87..4a22e17463fcc 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs @@ -30,7 +30,7 @@ pub(super) fn check<'cx>(cx: &LateContext<'cx>, name: Symbol, items: &[NestedMet cx, ALLOW_ATTRIBUTES_WITHOUT_REASON, attr.span, - &format!("`{}` attribute without specifying a reason", name.as_str()), + format!("`{}` attribute without specifying a reason", name.as_str()), None, "try adding a reason at the end with `, reason = \"..\"`", ); diff --git a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs index 3c5ac597fd5d4..3a8844d075485 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs @@ -31,6 +31,9 @@ fn check_duplicated_attr( attr_paths: &mut FxHashMap, parent: &mut Vec, ) { + if attr.span.from_expansion() { + return; + } let Some(ident) = attr.ident() else { return }; let name = ident.name; if name == sym::doc || name == sym::cfg_attr { @@ -38,6 +41,14 @@ fn check_duplicated_attr( // conditions are the same. return; } + if let Some(direct_parent) = parent.last() + && ["cfg", "cfg_attr"].contains(&direct_parent.as_str()) + && [sym::all, sym::not, sym::any].contains(&name) + { + // FIXME: We don't correctly check `cfg`s for now, so if it's more complex than just a one + // level `cfg`, we leave. + return; + } if let Some(value) = attr.value_str() { emit_if_duplicated(cx, attr, attr_paths, format!("{}:{name}={value}", parent.join(":"))); } else if let Some(sub_attrs) = attr.meta_item_list() { diff --git a/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs b/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs index cfcd2cc6a00b7..3b5b80ffefaf2 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs @@ -21,7 +21,7 @@ pub(super) fn check(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Att cx, INLINE_ALWAYS, attr.span, - &format!("you have declared `#[inline(always)]` on `{name}`. This is usually a bad idea"), + format!("you have declared `#[inline(always)]` on `{name}`. This is usually a bad idea"), ); } } diff --git a/src/tools/clippy/clippy_lints/src/attrs/maybe_misused_cfg.rs b/src/tools/clippy/clippy_lints/src/attrs/maybe_misused_cfg.rs index 5a70866eda587..e6b2e835be867 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/maybe_misused_cfg.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/maybe_misused_cfg.rs @@ -40,7 +40,7 @@ fn check_nested_misused_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) { cx, MAYBE_MISUSED_CFG, meta.span, - &format!("'test' may be misspelled as '{}'", ident.name.as_str()), + format!("'test' may be misspelled as '{}'", ident.name.as_str()), "did you mean", "test".to_string(), Applicability::MaybeIncorrect, 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 c2e21cfd33006..5d2ea36b366c1 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 @@ -1,30 +1,85 @@ use super::MIXED_ATTRIBUTES_STYLE; use clippy_utils::diagnostics::span_lint; -use rustc_ast::AttrStyle; -use rustc_lint::EarlyContext; +use rustc_ast::{AttrKind, AttrStyle, Attribute}; +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::sync::Lrc; +use rustc_lint::{LateContext, LintContext}; +use rustc_span::source_map::SourceMap; +use rustc_span::{SourceFile, Span, Symbol}; -pub(super) fn check(cx: &EarlyContext<'_>, item: &rustc_ast::Item) { - let mut has_outer = false; - let mut has_inner = false; +#[derive(Hash, PartialEq, Eq)] +enum SimpleAttrKind { + Doc, + /// A normal attribute, with its name symbols. + Normal(Vec), +} + +impl From<&AttrKind> for SimpleAttrKind { + fn from(value: &AttrKind) -> Self { + match value { + AttrKind::Normal(attr) => { + let path_symbols = attr + .item + .path + .segments + .iter() + .map(|seg| seg.ident.name) + .collect::>(); + Self::Normal(path_symbols) + }, + AttrKind::DocComment(..) => Self::Doc, + } + } +} + +pub(super) fn check(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute]) { + let mut inner_attr_kind: FxHashSet = FxHashSet::default(); + let mut outer_attr_kind: FxHashSet = FxHashSet::default(); + + let source_map = cx.sess().source_map(); + let item_src = source_map.lookup_source_file(item_span.lo()); - for attr in &item.attrs { - if attr.span.from_expansion() { + for attr in attrs { + if attr.span.from_expansion() || !attr_in_same_src_as_item(source_map, &item_src, attr.span) { continue; } + + let kind: SimpleAttrKind = (&attr.kind).into(); match attr.style { - AttrStyle::Inner => has_inner = true, - AttrStyle::Outer => has_outer = true, - } + AttrStyle::Inner => { + if outer_attr_kind.contains(&kind) { + lint_mixed_attrs(cx, attrs); + return; + } + inner_attr_kind.insert(kind); + }, + AttrStyle::Outer => { + if inner_attr_kind.contains(&kind) { + lint_mixed_attrs(cx, attrs); + return; + } + outer_attr_kind.insert(kind); + }, + }; } - if !has_outer || !has_inner { +} + +fn lint_mixed_attrs(cx: &LateContext<'_>, 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()) { + first.span.with_hi(last.span.hi()) + } else { return; - } - let mut attrs_iter = item.attrs.iter().filter(|attr| !attr.span.from_expansion()); - let span = attrs_iter.next().unwrap().span; + }; span_lint( cx, MIXED_ATTRIBUTES_STYLE, - span.with_hi(attrs_iter.last().unwrap().span.hi()), + span, "item has both inner and outer attributes", ); } + +fn attr_in_same_src_as_item(source_map: &SourceMap, item_src: &Lrc, attr_span: Span) -> bool { + let attr_src = source_map.lookup_source_file(attr_span.lo()); + Lrc::ptr_eq(item_src, &attr_src) +} diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs index 675c428948f68..684ad7de2f05c 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs @@ -465,10 +465,20 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks that an item has only one kind of attributes. + /// Checks for items that have the same kind of attributes with mixed styles (inner/outer). /// /// ### Why is this bad? - /// Having both kinds of attributes makes it more complicated to read code. + /// Having both style of said attributes makes it more complicated to read code. + /// + /// ### Known problems + /// This lint currently has false-negatives when mixing same attributes + /// but they have different path symbols, for example: + /// ```ignore + /// #[custom_attribute] + /// pub fn foo() { + /// #![my_crate::custom_attribute] + /// } + /// ``` /// /// ### Example /// ```no_run @@ -486,7 +496,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.78.0"] pub MIXED_ATTRIBUTES_STYLE, - suspicious, + style, "item has both inner and outer attributes" } @@ -523,6 +533,7 @@ declare_lint_pass!(Attributes => [ USELESS_ATTRIBUTE, BLANKET_CLIPPY_RESTRICTION_LINTS, SHOULD_PANIC_WITHOUT_EXPECT, + MIXED_ATTRIBUTES_STYLE, ]); impl<'tcx> LateLintPass<'tcx> for Attributes { @@ -566,6 +577,7 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { ItemKind::ExternCrate(..) | ItemKind::Use(..) => useless_attribute::check(cx, item, attrs), _ => {}, } + mixed_attributes_style::check(cx, item.span, attrs); } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { @@ -594,7 +606,6 @@ impl_lint_pass!(EarlyAttributes => [ MAYBE_MISUSED_CFG, DEPRECATED_CLIPPY_CFG_ATTR, UNNECESSARY_CLIPPY_CFG, - MIXED_ATTRIBUTES_STYLE, DUPLICATED_ATTRIBUTES, ]); @@ -605,7 +616,6 @@ impl EarlyLintPass for EarlyAttributes { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::Item) { empty_line_after::check(cx, item); - mixed_attributes_style::check(cx, item); duplicated_attributes::check(cx, &item.attrs); } diff --git a/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs b/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs index 05da69636c62f..486e7c6ec4f4a 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs @@ -58,7 +58,7 @@ pub(super) fn check( clippy_lints, "no need to put clippy lints behind a `clippy` cfg", None, - &format!( + format!( "write instead: `#{}[{}({})]`", if attr.style == AttrStyle::Inner { "!" } else { "" }, ident.name, diff --git a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs index 765cc7c0a54fb..f25a474d9bbd6 100644 --- a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs +++ b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs @@ -267,7 +267,7 @@ fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed: &DisallowedPa cx, AWAIT_HOLDING_INVALID_TYPE, span, - &format!( + format!( "`{}` may not be held across an `await` point per `clippy.toml`", disallowed.path() ), diff --git a/src/tools/clippy/clippy_lints/src/blocks_in_conditions.rs b/src/tools/clippy/clippy_lints/src/blocks_in_conditions.rs index 2eb0dac974258..171f303186012 100644 --- a/src/tools/clippy/clippy_lints/src/blocks_in_conditions.rs +++ b/src/tools/clippy/clippy_lints/src/blocks_in_conditions.rs @@ -72,7 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInConditions { else { return; }; - let complex_block_message = &format!( + let complex_block_message = format!( "in {desc}, avoid complex blocks or closures with blocks; \ instead, move the block or closure higher and bind it with a `let`", ); @@ -141,7 +141,7 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInConditions { let ex = &body.value; if let ExprKind::Block(block, _) = ex.kind { if !body.value.span.from_expansion() && !block.stmts.is_empty() { - span_lint(cx, BLOCKS_IN_CONDITIONS, ex.span, complex_block_message); + span_lint(cx, BLOCKS_IN_CONDITIONS, ex.span, complex_block_message.clone()); return ControlFlow::Continue(Descend::No); } } diff --git a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs index 74201e9cc304d..58c1a2f270621 100644 --- a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs +++ b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs @@ -121,7 +121,7 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { cx, BOOL_ASSERT_COMPARISON, macro_call.span, - &format!("used `{macro_name}!` with a literal bool"), + format!("used `{macro_name}!` with a literal bool"), |diag| { // assert_eq!(...) // ^^^^^^^^^ diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs index a474356608fbe..6edfebb5534f2 100644 --- a/src/tools/clippy/clippy_lints/src/booleans.rs +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -392,13 +392,13 @@ fn simple_negate(b: Bool) -> Bool { t @ Term(_) => Not(Box::new(t)), And(mut v) => { for el in &mut v { - *el = simple_negate(::std::mem::replace(el, True)); + *el = simple_negate(std::mem::replace(el, True)); } Or(v) }, Or(mut v) => { for el in &mut v { - *el = simple_negate(::std::mem::replace(el, True)); + *el = simple_negate(std::mem::replace(el, True)); } And(v) }, diff --git a/src/tools/clippy/clippy_lints/src/box_default.rs b/src/tools/clippy/clippy_lints/src/box_default.rs index 8683cb86e8ac8..4062212f408ef 100644 --- a/src/tools/clippy/clippy_lints/src/box_default.rs +++ b/src/tools/clippy/clippy_lints/src/box_default.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::macro_backtrace; -use clippy_utils::source::snippet_opt; use clippy_utils::ty::expr_sig; use clippy_utils::{is_default_equivalent, path_def_id}; use rustc_errors::Applicability; @@ -9,20 +8,16 @@ use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::{Block, Expr, ExprKind, LetStmt, Node, QPath, Ty, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::print::with_forced_trimmed_paths; -use rustc_middle::ty::IsSuggestable; use rustc_session::declare_lint_pass; use rustc_span::sym; declare_clippy_lint! { /// ### What it does - /// checks for `Box::new(T::default())`, which is better written as - /// `Box::::default()`. + /// checks for `Box::new(Default::default())`, which can be written as + /// `Box::default()`. /// /// ### Why is this bad? - /// First, it's more complex, involving two calls instead of one. - /// Second, `Box::default()` can be faster - /// [in certain cases](https://nnethercote.github.io/perf-book/standard-library-types.html#box). + /// `Box::default()` is equivalent and more concise. /// /// ### Example /// ```no_run @@ -34,7 +29,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.66.0"] pub BOX_DEFAULT, - perf, + style, "Using Box::new(T::default()) instead of Box::default()" } @@ -53,14 +48,14 @@ impl LateLintPass<'_> for BoxDefault { && path_def_id(cx, ty).map_or(false, |id| Some(id) == cx.tcx.lang_items().owned_box()) // And the single argument to the call is another function call // This is the `T::default()` of `Box::new(T::default())` - && let ExprKind::Call(arg_path, inner_call_args) = arg.kind + && let ExprKind::Call(arg_path, _) = arg.kind // And we are not in a foreign crate's macro && !in_external_macro(cx.sess(), expr.span) // And the argument expression has the same context as the outer call expression // or that we are inside a `vec!` macro expansion && (expr.span.eq_ctxt(arg.span) || is_local_vec_expn(cx, arg, expr)) - // And the argument is equivalent to `Default::default()` - && is_default_equivalent(cx, arg) + // And the argument is `Default::default()` or the type is specified + && (is_plain_default(cx, arg_path) || (given_type(cx, expr) && is_default_equivalent(cx, arg))) { span_lint_and_sugg( cx, @@ -68,25 +63,7 @@ impl LateLintPass<'_> for BoxDefault { expr.span, "`Box::new(_)` of default value", "try", - if is_plain_default(cx, arg_path) || given_type(cx, expr) { - "Box::default()".into() - } else if let Some(arg_ty) = - cx.typeck_results().expr_ty(arg).make_suggestable(cx.tcx, true, None) - { - // Check if we can copy from the source expression in the replacement. - // We need the call to have no argument (see `explicit_default_type`). - if inner_call_args.is_empty() - && let Some(ty) = explicit_default_type(arg_path) - && let Some(s) = snippet_opt(cx, ty.span) - { - format!("Box::<{s}>::default()") - } else { - // Otherwise, use the inferred type's formatting. - with_forced_trimmed_paths!(format!("Box::<{arg_ty}>::default()")) - } - } else { - return; - }, + "Box::default()".into(), Applicability::MachineApplicable, ); } @@ -105,20 +82,6 @@ fn is_plain_default(cx: &LateContext<'_>, arg_path: &Expr<'_>) -> bool { } } -// Checks whether the call is of the form `A::B::f()`. Returns `A::B` if it is. -// -// In the event we have this kind of construct, it's easy to use `A::B` as a replacement in the -// quickfix. `f` must however have no parameter. Should `f` have some, then some of the type of -// `A::B` may be inferred from the arguments. This would be the case for `Vec::from([0; false])`, -// where the argument to `from` allows inferring this is a `Vec` -fn explicit_default_type<'a>(arg_path: &'a Expr<'_>) -> Option<&'a Ty<'a>> { - if let ExprKind::Path(QPath::TypeRelative(ty, _)) = &arg_path.kind { - Some(ty) - } else { - None - } -} - fn is_local_vec_expn(cx: &LateContext<'_>, expr: &Expr<'_>, ref_expr: &Expr<'_>) -> bool { macro_backtrace(expr.span).next().map_or(false, |call| { cx.tcx.is_diagnostic_item(sym::vec_macro, call.def_id) && call.span.eq_ctxt(ref_expr.span) @@ -129,7 +92,7 @@ fn is_local_vec_expn(cx: &LateContext<'_>, expr: &Expr<'_>, ref_expr: &Expr<'_>) struct InferVisitor(bool); impl<'tcx> Visitor<'tcx> for InferVisitor { - fn visit_ty(&mut self, t: &rustc_hir::Ty<'_>) { + fn visit_ty(&mut self, t: &Ty<'_>) { self.0 |= matches!(t.kind, TyKind::Infer | TyKind::OpaqueDef(..) | TyKind::TraitObject(..)); if !self.0 { walk_ty(self, t); diff --git a/src/tools/clippy/clippy_lints/src/cargo/common_metadata.rs b/src/tools/clippy/clippy_lints/src/cargo/common_metadata.rs index 99fe6c1e790e5..3af2d8c025684 100644 --- a/src/tools/clippy/clippy_lints/src/cargo/common_metadata.rs +++ b/src/tools/clippy/clippy_lints/src/cargo/common_metadata.rs @@ -41,7 +41,7 @@ pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata, ignore_publish: b fn missing_warning(cx: &LateContext<'_>, package: &cargo_metadata::Package, field: &str) { let message = format!("package `{}` is missing `{field}` metadata", package.name); - span_lint(cx, CARGO_COMMON_METADATA, DUMMY_SP, &message); + span_lint(cx, CARGO_COMMON_METADATA, DUMMY_SP, message); } fn is_empty_str>(value: &Option) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/cargo/feature_name.rs b/src/tools/clippy/clippy_lints/src/cargo/feature_name.rs index 9e69919c72737..6982b96dd3b3c 100644 --- a/src/tools/clippy/clippy_lints/src/cargo/feature_name.rs +++ b/src/tools/clippy/clippy_lints/src/cargo/feature_name.rs @@ -56,13 +56,13 @@ fn lint(cx: &LateContext<'_>, feature: &str, substring: &str, is_prefix: bool) { REDUNDANT_FEATURE_NAMES }, DUMMY_SP, - &format!( + format!( "the \"{substring}\" {} in the feature name \"{feature}\" is {}", if is_prefix { "prefix" } else { "suffix" }, if is_negative { "negative" } else { "redundant" } ), None, - &format!( + format!( "consider renaming the feature to \"{}\"{}", if is_prefix { feature.strip_prefix(substring) diff --git a/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs b/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs index a39b972b56a23..a3291c9da1096 100644 --- a/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs +++ b/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs @@ -102,7 +102,7 @@ fn check_table(cx: &LateContext<'_>, table: LintTable, groups: &FxHashSet<&str>, cx, LINT_GROUPS_PRIORITY, toml_span(group.span(), file), - &format!( + format!( "lint group `{}` has the same priority ({priority}) as a lint", group.as_ref() ), diff --git a/src/tools/clippy/clippy_lints/src/cargo/mod.rs b/src/tools/clippy/clippy_lints/src/cargo/mod.rs index 95d5449781b47..ca7fa4e5a4109 100644 --- a/src/tools/clippy/clippy_lints/src/cargo/mod.rs +++ b/src/tools/clippy/clippy_lints/src/cargo/mod.rs @@ -241,7 +241,7 @@ impl LateLintPass<'_> for Cargo { }, Err(e) => { for lint in NO_DEPS_LINTS { - span_lint(cx, lint, DUMMY_SP, &format!("could not read cargo metadata: {e}")); + span_lint(cx, lint, DUMMY_SP, format!("could not read cargo metadata: {e}")); } }, } @@ -257,7 +257,7 @@ impl LateLintPass<'_> for Cargo { }, Err(e) => { for lint in WITH_DEPS_LINTS { - span_lint(cx, lint, DUMMY_SP, &format!("could not read cargo metadata: {e}")); + span_lint(cx, lint, DUMMY_SP, format!("could not read cargo metadata: {e}")); } }, } diff --git a/src/tools/clippy/clippy_lints/src/cargo/multiple_crate_versions.rs b/src/tools/clippy/clippy_lints/src/cargo/multiple_crate_versions.rs index 3f30a77fcfe47..2769463c8a53c 100644 --- a/src/tools/clippy/clippy_lints/src/cargo/multiple_crate_versions.rs +++ b/src/tools/clippy/clippy_lints/src/cargo/multiple_crate_versions.rs @@ -52,7 +52,7 @@ pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata, allowed_duplicate cx, MULTIPLE_CRATE_VERSIONS, DUMMY_SP, - &format!("multiple versions for dependency `{name}`: {versions}"), + format!("multiple versions for dependency `{name}`: {versions}"), ); } } diff --git a/src/tools/clippy/clippy_lints/src/cargo/wildcard_dependencies.rs b/src/tools/clippy/clippy_lints/src/cargo/wildcard_dependencies.rs index 244e98eb66662..0cf687d01928c 100644 --- a/src/tools/clippy/clippy_lints/src/cargo/wildcard_dependencies.rs +++ b/src/tools/clippy/clippy_lints/src/cargo/wildcard_dependencies.rs @@ -17,7 +17,7 @@ pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata) { cx, WILDCARD_DEPENDENCIES, DUMMY_SP, - &format!("wildcard dependency for `{}`", dep.name), + format!("wildcard dependency for `{}`", dep.name), ); } } diff --git a/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs b/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs index a667ea04af0ac..f05fd3fcde500 100644 --- a/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs +++ b/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs @@ -28,7 +28,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cx, AS_PTR_CAST_MUT, expr.span, - &format!("casting the result of `as_ptr` to *mut {ptrty}"), + format!("casting the result of `as_ptr` to *mut {ptrty}"), "replace with", format!("{recv}.as_mut_ptr()"), applicability, diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs b/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs index c166334832146..d4d5ee37bccbb 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs @@ -34,7 +34,7 @@ pub(super) fn check( cx, CAST_ABS_TO_UNSIGNED, span, - &format!("casting the result of `{cast_from}::abs()` to {cast_to}"), + format!("casting the result of `{cast_from}::abs()` to {cast_to}"), "replace with", format!("{}.unsigned_abs()", Sugg::hir(cx, receiver, "..").maybe_par()), Applicability::MachineApplicable, diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs index 86f4332d05aad..d52ad1c6f23f5 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs @@ -68,7 +68,7 @@ pub(super) fn check( cx, CAST_LOSSLESS, expr.span, - &message, + message, "try", format!("{cast_to_fmt}::from({sugg})"), app, diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_nan_to_int.rs b/src/tools/clippy/clippy_lints/src/casts/cast_nan_to_int.rs index da756129db3ae..1743ce71adde4 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_nan_to_int.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_nan_to_int.rs @@ -12,7 +12,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cx, CAST_NAN_TO_INT, expr.span, - &format!("casting a known NaN to {to_ty}"), + format!("casting a known NaN to {to_ty}"), None, "this always evaluates to 0", ); diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs index 2c0a3d4829608..dbfa8e1ee91b2 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs @@ -41,7 +41,7 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b }) }, BinOpKind::Rem | BinOpKind::BitAnd => get_constant_bits(cx, right) - .unwrap_or(u64::max_value()) + .unwrap_or(u64::MAX) .min(apply_reductions(cx, nbits, left, signed)), BinOpKind::Shr => apply_reductions(cx, nbits, left, signed) .saturating_sub(constant_int(cx, right).map_or(0, |s| u64::try_from(s).unwrap_or_default())), @@ -56,7 +56,7 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b } else { None }; - apply_reductions(cx, nbits, left, signed).min(max_bits.unwrap_or(u64::max_value())) + apply_reductions(cx, nbits, left, signed).min(max_bits.unwrap_or(u64::MAX)) }, ExprKind::MethodCall(method, _, [lo, hi], _) => { if method.ident.as_str() == "clamp" { @@ -142,7 +142,7 @@ pub(super) fn check( cx, CAST_ENUM_TRUNCATION, expr.span, - &format!( + format!( "casting `{cast_from}::{}` to `{cast_to}` will truncate the value{suffix}", variant.name, ), @@ -163,7 +163,7 @@ pub(super) fn check( _ => return, }; - span_lint_and_then(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg, |diag| { + span_lint_and_then(cx, CAST_POSSIBLE_TRUNCATION, expr.span, msg, |diag| { diag.help("if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ..."); if !cast_from.is_floating_point() { offer_suggestion(cx, expr, cast_expr, cast_to_span, diag); diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs index 2ddb0f00ecdd5..11274383595a8 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs @@ -79,7 +79,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca ), }; - span_lint_and_then(cx, CAST_POSSIBLE_WRAP, expr.span, &message, |diag| { + span_lint_and_then(cx, CAST_POSSIBLE_WRAP, expr.span, message, |diag| { if let EmitState::LintOnPtrSize(16) = should_lint { diag .note("`usize` and `isize` may be as small as 16 bits on some platforms") diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_precision_loss.rs b/src/tools/clippy/clippy_lints/src/casts/cast_precision_loss.rs index 334e1646cd4fc..035666e4d4c92 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_precision_loss.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_precision_loss.rs @@ -38,7 +38,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca cx, CAST_PRECISION_LOSS, expr.span, - &format!( + format!( "casting `{0}` to `{1}` causes a loss of precision {2}(`{0}` is {3} bits wide, \ but `{1}`'s mantissa is only {4} bits wide)", cast_from, diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs index 4d1a0f678f4b0..960c81045e36f 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -48,7 +48,7 @@ fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_f cx, CAST_PTR_ALIGNMENT, expr.span, - &format!( + format!( "casting from `{cast_from}` to a more-strictly-aligned pointer (`{cast_to}`) ({} < {} bytes)", from_layout.align.abi.bytes(), to_layout.align.abi.bytes(), diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs index 8fd95d9654cf0..2b6e17dc1030d 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs @@ -47,7 +47,7 @@ pub(super) fn check<'cx>( cx, CAST_SIGN_LOSS, expr.span, - &format!("casting `{cast_from}` to `{cast_to}` may lose the sign of the value"), + format!("casting `{cast_from}` to `{cast_to}` may lose the sign of the value"), ); } } @@ -118,7 +118,7 @@ enum Sign { Uncertain, } -fn expr_sign<'cx>(cx: &LateContext<'cx>, expr: &Expr<'_>, ty: impl Into>>) -> Sign { +fn expr_sign<'cx, 'tcx>(cx: &LateContext<'cx>, mut expr: &'tcx Expr<'tcx>, ty: impl Into>>) -> Sign { // Try evaluate this expr first to see if it's positive if let Some(val) = get_const_signed_int_eval(cx, expr, ty) { return if val >= 0 { Sign::ZeroOrPositive } else { Sign::Negative }; @@ -134,11 +134,12 @@ fn expr_sign<'cx>(cx: &LateContext<'cx>, expr: &Expr<'_>, ty: impl Into(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Msrv cx, CAST_SLICE_DIFFERENT_SIZES, expr.span, - &format!( + format!( "casting between raw pointers to `[{}]` (element size {from_size}) and `[{}]` (element size {to_size}) does not adjust the count", start_ty.ty, end_ty.ty, ), diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs b/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs index 48629b6c5ccd4..1d89f6c75e188 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs @@ -46,7 +46,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cx, CAST_SLICE_FROM_RAW_PARTS, span, - &format!("casting the result of `{func}` to {cast_to}"), + format!("casting the result of `{func}` to {cast_to}"), "replace with", format!("core::ptr::slice_{func}({ptr}, {len})"), applicability, diff --git a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs index a26bfab4e7c15..f263bec1576d0 100644 --- a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs @@ -25,7 +25,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cx, FN_TO_NUMERIC_CAST, expr.span, - &format!("casting function pointer `{from_snippet}` to `{cast_to}`"), + format!("casting function pointer `{from_snippet}` to `{cast_to}`"), "try", format!("{from_snippet} as usize"), applicability, diff --git a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_any.rs b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_any.rs index 75654129408e6..826589bf303b5 100644 --- a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_any.rs +++ b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_any.rs @@ -23,7 +23,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cx, FN_TO_NUMERIC_CAST_ANY, expr.span, - &format!("casting function pointer `{from_snippet}` to `{cast_to}`"), + format!("casting function pointer `{from_snippet}` to `{cast_to}`"), "did you mean to invoke the function?", format!("{from_snippet}() as {cast_to}"), applicability, diff --git a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs index 556be1d150665..0e11bcfb8ecdb 100644 --- a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs +++ b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs @@ -24,7 +24,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cx, FN_TO_NUMERIC_CAST_WITH_TRUNCATION, expr.span, - &format!("casting function pointer `{from_snippet}` to `{cast_to}`, which truncates the value"), + format!("casting function pointer `{from_snippet}` to `{cast_to}`, which truncates the value"), "try", format!("{from_snippet} as usize"), applicability, diff --git a/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs index 5a121e6a7eb3a..68841076f7700 100644 --- a/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs @@ -62,8 +62,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) { // we omit following `cast`: let omit_cast = if let ExprKind::Call(func, []) = cast_expr.kind && let ExprKind::Path(ref qpath @ QPath::Resolved(None, path)) = func.kind + && let Some(method_defid) = path.res.opt_def_id() { - let method_defid = path.res.def_id(); if cx.tcx.is_diagnostic_item(sym::ptr_null, method_defid) { OmitFollowedCastReason::Null(qpath) } else if cx.tcx.is_diagnostic_item(sym::ptr_null_mut, method_defid) { diff --git a/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs index e88146331cae1..921693567fcd4 100644 --- a/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs +++ b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs @@ -36,7 +36,7 @@ pub(super) fn check<'tcx>( PTR_CAST_CONSTNESS, expr.span, "`as` casting between raw pointers while changing only its constness", - &format!("try `pointer::cast_{constness}`, a safer alternative"), + format!("try `pointer::cast_{constness}`, a safer alternative"), format!("{}.cast_{constness}()", sugg.maybe_par()), Applicability::MachineApplicable, ); diff --git a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs index 148d52cb5ddca..a7f7bf7854e65 100644 --- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs @@ -51,7 +51,7 @@ pub(super) fn check<'tcx>( cx, UNNECESSARY_CAST, expr.span, - &format!( + format!( "casting raw pointers to the same type and constness is unnecessary (`{cast_from}` -> `{cast_to}`)" ), "try", @@ -166,7 +166,7 @@ pub(super) fn check<'tcx>( cx, UNNECESSARY_CAST, expr.span, - &format!("casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)"), + format!("casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)"), "try", if needs_block { format!("{{ {cast_str} }}") @@ -209,7 +209,7 @@ fn lint_unnecessary_cast( cx, UNNECESSARY_CAST, expr.span, - &format!("casting {literal_kind_name} literal to `{cast_to}` is unnecessary"), + format!("casting {literal_kind_name} literal to `{cast_to}` is unnecessary"), "try", sugg, Applicability::MachineApplicable, diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs index 7dac3c5d9dabc..ee1bb63b50d30 100644 --- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs @@ -121,7 +121,7 @@ impl CognitiveComplexity { cx, COGNITIVE_COMPLEXITY, fn_span, - &format!( + format!( "the function has a cognitive complexity of ({cc}/{})", self.limit.limit() ), diff --git a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs index e921b9b46a674..6942ca5364042 100644 --- a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs +++ b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs @@ -70,7 +70,7 @@ impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead { } } -fn match_acceptable_type(cx: &LateContext<'_>, local: &LetStmt<'_>, collections: &[rustc_span::Symbol]) -> bool { +fn match_acceptable_type(cx: &LateContext<'_>, local: &LetStmt<'_>, collections: &[Symbol]) -> bool { let ty = cx.typeck_results().pat_ty(local.pat); collections.iter().any(|&sym| is_type_diagnostic_item(cx, ty, sym)) // String type is a lang item but not a diagnostic item for now so we need a separate check diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index c8e148598a272..5ff7d8e513435 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -254,6 +254,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::large_include_file::LARGE_INCLUDE_FILE_INFO, crate::large_stack_arrays::LARGE_STACK_ARRAYS_INFO, crate::large_stack_frames::LARGE_STACK_FRAMES_INFO, + crate::legacy_numeric_constants::LEGACY_NUMERIC_CONSTANTS_INFO, crate::len_zero::COMPARISON_TO_EMPTY_INFO, crate::len_zero::LEN_WITHOUT_IS_EMPTY_INFO, crate::len_zero::LEN_ZERO_INFO, @@ -678,6 +679,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::trait_bounds::TYPE_REPETITION_IN_BOUNDS_INFO, crate::transmute::CROSSPOINTER_TRANSMUTE_INFO, crate::transmute::EAGER_TRANSMUTE_INFO, + crate::transmute::MISSING_TRANSMUTE_ANNOTATIONS_INFO, crate::transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS_INFO, crate::transmute::TRANSMUTE_BYTES_TO_STR_INFO, crate::transmute::TRANSMUTE_FLOAT_TO_INT_INFO, diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs index 98a6d9370c344..2b3f4854255cf 100644 --- a/src/tools/clippy/clippy_lints/src/default.rs +++ b/src/tools/clippy/clippy_lints/src/default.rs @@ -100,7 +100,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { cx, DEFAULT_TRAIT_ACCESS, expr.span, - &format!("calling `{replacement}` is more clear than this expression"), + format!("calling `{replacement}` is more clear than this expression"), "try", replacement, Applicability::Unspecified, // First resolve the TODO above @@ -243,7 +243,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { first_assign.unwrap().span, "field assignment outside of initializer for an instance created with Default::default()", Some(local.span), - &format!("consider initializing the variable with `{sugg}` and removing relevant reassignments"), + format!("consider initializing the variable with `{sugg}` and removing relevant reassignments"), ); self.reassigned_linted.insert(span); } diff --git a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs index 71e1a25c2bce4..137781754966a 100644 --- a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs +++ b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs @@ -56,7 +56,7 @@ fn is_alias(ty: hir::Ty<'_>) -> bool { impl LateLintPass<'_> for DefaultConstructedUnitStructs { fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { - if let hir::ExprKind::Call(fn_expr, &[]) = expr.kind + if let ExprKind::Call(fn_expr, &[]) = expr.kind // make sure we have a call to `Default::default` && let ExprKind::Path(ref qpath @ hir::QPath::TypeRelative(base, _)) = fn_expr.kind // make sure this isn't a type alias: diff --git a/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs b/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs index e617c19eff09b..ac49e6f1a482a 100644 --- a/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs +++ b/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs @@ -49,7 +49,7 @@ impl<'tcx> LateLintPass<'tcx> for DefaultIterEmpty { cx, DEFAULT_INSTEAD_OF_ITER_EMPTY, expr.span, - &format!("`{path}()` is the more idiomatic way"), + format!("`{path}()` is the more idiomatic way"), "try", sugg, applicability, @@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for DefaultIterEmpty { fn make_sugg( cx: &LateContext<'_>, - ty_path: &rustc_hir::QPath<'_>, + ty_path: &QPath<'_>, ctxt: SyntaxContext, applicability: &mut Applicability, path: &str, diff --git a/src/tools/clippy/clippy_lints/src/default_union_representation.rs b/src/tools/clippy/clippy_lints/src/default_union_representation.rs index bfd89bfd2c72f..3f87ed8df2bf3 100644 --- a/src/tools/clippy/clippy_lints/src/default_union_representation.rs +++ b/src/tools/clippy/clippy_lints/src/default_union_representation.rs @@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for DefaultUnionRepresentation { item.span, "this union has the default representation", None, - &format!( + format!( "consider annotating `{}` with `#[repr(C)]` to explicitly specify memory layout", cx.tcx.def_path_str(item.owner_id) ), diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs index b0f46f5c646cf..80327586fedcc 100644 --- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs +++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs @@ -79,7 +79,7 @@ fn is_path_self(e: &Expr<'_>) -> bool { fn contains_trait_object(ty: Ty<'_>) -> bool { match ty.kind() { ty::Ref(_, ty, _) => contains_trait_object(*ty), - ty::Adt(def, args) => def.is_box() && args[0].as_type().map_or(false, contains_trait_object), + Adt(def, args) => def.is_box() && args[0].as_type().map_or(false, contains_trait_object), ty::Dynamic(..) => true, _ => false, } diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs index c554edc8fceba..5f9700b76d942 100644 --- a/src/tools/clippy/clippy_lints/src/derive.rs +++ b/src/tools/clippy/clippy_lints/src/derive.rs @@ -11,8 +11,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::traits::Reveal; use rustc_middle::ty::{ - self, ClauseKind, GenericArgKind, GenericParamDefKind, ParamEnv, ToPredicate, TraitPredicate, Ty, - TyCtxt, + self, ClauseKind, GenericArgKind, GenericParamDefKind, ParamEnv, ToPredicate, TraitPredicate, Ty, TyCtxt, }; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; diff --git a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs index 4a617ba34d57b..871f529da6c40 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs @@ -102,11 +102,11 @@ impl DisallowedMacros { DISALLOWED_MACROS, cx.tcx.local_def_id_to_hir_id(derive_src.def_id), mac.span, - &msg, + msg, add_note, ); } else { - span_lint_and_then(cx, DISALLOWED_MACROS, mac.span, &msg, add_note); + span_lint_and_then(cx, DISALLOWED_MACROS, mac.span, msg, add_note); } } } diff --git a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs index 1868d3cd39156..9de879604e2ec 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs @@ -99,7 +99,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { None => return, }; let msg = format!("use of a disallowed method `{}`", conf.path()); - span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, &msg, |diag| { + span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, msg, |diag| { if let Some(reason) = conf.reason() { diag.note(reason); } diff --git a/src/tools/clippy/clippy_lints/src/disallowed_names.rs b/src/tools/clippy/clippy_lints/src/disallowed_names.rs index 09dad5554ad73..2afbf184117e1 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_names.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_names.rs @@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedNames { cx, DISALLOWED_NAMES, ident.span, - &format!("use of a disallowed/placeholder name `{}`", ident.name), + format!("use of a disallowed/placeholder name `{}`", ident.name), ); } } diff --git a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs index 0c1bb2da7e896..def4b5932b4b1 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs @@ -98,7 +98,7 @@ impl EarlyLintPass for DisallowedScriptIdents { cx, DISALLOWED_SCRIPT_IDENTS, span, - &format!( + format!( "identifier `{symbol_str}` has a Unicode script that is not allowed by configuration: {}", script.full_name() ), diff --git a/src/tools/clippy/clippy_lints/src/disallowed_types.rs b/src/tools/clippy/clippy_lints/src/disallowed_types.rs index 130f56b698ffc..4196309a22a40 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_types.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_types.rs @@ -127,7 +127,7 @@ fn emit(cx: &LateContext<'_>, name: &str, span: Span, conf: &DisallowedPath) { cx, DISALLOWED_TYPES, span, - &format!("`{name}` is not allowed according to config"), + format!("`{name}` is not allowed according to config"), |diag| { if let Some(reason) = conf.reason() { diag.note(reason); diff --git a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs index bf6f54c1e72a3..119473c2454be 100644 --- a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs +++ b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs @@ -129,9 +129,9 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { cx, lint, expr.span, - &msg, + msg, note_span, - &format!("argument has type `{arg_ty}`"), + format!("argument has type `{arg_ty}`"), ); } } diff --git a/src/tools/clippy/clippy_lints/src/duplicate_mod.rs b/src/tools/clippy/clippy_lints/src/duplicate_mod.rs index 471335c098fc4..ed27e38ef2d19 100644 --- a/src/tools/clippy/clippy_lints/src/duplicate_mod.rs +++ b/src/tools/clippy/clippy_lints/src/duplicate_mod.rs @@ -119,7 +119,7 @@ impl EarlyLintPass for DuplicateMod { cx, DUPLICATE_MOD, multi_span, - &format!("file is loaded as a module multiple times: `{}`", local_path.display()), + format!("file is loaded as a module multiple times: `{}`", local_path.display()), None, "replace all but one `mod` item with `use` items", ); diff --git a/src/tools/clippy/clippy_lints/src/endian_bytes.rs b/src/tools/clippy/clippy_lints/src/endian_bytes.rs index b8a817e21b148..dd03df797de3f 100644 --- a/src/tools/clippy/clippy_lints/src/endian_bytes.rs +++ b/src/tools/clippy/clippy_lints/src/endian_bytes.rs @@ -197,7 +197,7 @@ fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix cx, lint.as_lint(), expr.span, - &format!( + format!( "usage of the {}`{ty}::{}`{}", if prefix == Prefix::From { "function " } else { "" }, lint.as_name(prefix), diff --git a/src/tools/clippy/clippy_lints/src/entry.rs b/src/tools/clippy/clippy_lints/src/entry.rs index dafbf6c884698..b7c9d3d03852f 100644 --- a/src/tools/clippy/clippy_lints/src/entry.rs +++ b/src/tools/clippy/clippy_lints/src/entry.rs @@ -186,7 +186,7 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { cx, MAP_ENTRY, expr.span, - &format!("usage of `contains_key` followed by `insert` on a `{}`", map_ty.name()), + format!("usage of `contains_key` followed by `insert` on a `{}`", map_ty.name()), "try", sugg, app, diff --git a/src/tools/clippy/clippy_lints/src/escape.rs b/src/tools/clippy/clippy_lints/src/escape.rs index ad589dad350b3..386d4c3c317f6 100644 --- a/src/tools/clippy/clippy_lints/src/escape.rs +++ b/src/tools/clippy/clippy_lints/src/escape.rs @@ -177,7 +177,7 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { } } - fn fake_read(&mut self, _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} + fn fake_read(&mut self, _: &PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> { diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index eccfc31fdd3e3..850a4f0eec8eb 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -3,14 +3,14 @@ use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet_opt; use clippy_utils::ty::type_diagnostic_name; use clippy_utils::usage::{local_used_after_expr, local_used_in}; -use clippy_utils::{get_path_from_caller_to_method_type, higher, is_adjusted, path_to_local, path_to_local_id}; +use clippy_utils::{get_path_from_caller_to_method_type, is_adjusted, path_to_local, path_to_local_id}; use rustc_errors::Applicability; use rustc_hir::{BindingAnnotation, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, TyKind, Unsafety}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{ - self, Binder, ClosureArgs, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, RegionKind, - Ty, TypeVisitableExt, TypeckResults, + self, Binder, ClosureArgs, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, RegionKind, Ty, + TypeVisitableExt, TypeckResults, }; use rustc_session::declare_lint_pass; use rustc_span::symbol::sym; @@ -88,7 +88,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { if body.value.span.from_expansion() { if body.params.is_empty() { - if let Some(VecArgs::Vec(&[])) = higher::VecArgs::hir(cx, body.value) { + if let Some(VecArgs::Vec(&[])) = VecArgs::hir(cx, body.value) { // replace `|| vec![]` with `Vec::new` span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/excessive_bools.rs b/src/tools/clippy/clippy_lints/src/excessive_bools.rs index c5f7212c4c081..62d5ce24d40a6 100644 --- a/src/tools/clippy/clippy_lints/src/excessive_bools.rs +++ b/src/tools/clippy/clippy_lints/src/excessive_bools.rs @@ -120,7 +120,7 @@ impl ExcessiveBools { cx, FN_PARAMS_EXCESSIVE_BOOLS, span, - &format!("more than {} bools in function parameters", self.max_fn_params_bools), + format!("more than {} bools in function parameters", self.max_fn_params_bools), None, "consider refactoring bools into two-variant enums", ); @@ -145,7 +145,7 @@ impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { cx, STRUCT_EXCESSIVE_BOOLS, item.span, - &format!("more than {} bools in a struct", self.max_struct_bools), + format!("more than {} bools in a struct", self.max_struct_bools), None, "consider using a state machine or refactoring bools into two-variant enums", ); diff --git a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs index 3a621d967f43f..9ffda64574243 100644 --- a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs +++ b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs @@ -38,7 +38,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Warns on any exported `structs`s that are not tagged `#[non_exhaustive]` + /// Warns on any exported `struct`s that are not tagged `#[non_exhaustive]` /// /// ### Why is this bad? /// Exhaustive structs are typically fine, but a project which does diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs index 2e9bec6a7b083..33bd5a5a9d3a3 100644 --- a/src/tools/clippy/clippy_lints/src/explicit_write.rs +++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs @@ -88,7 +88,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { cx, EXPLICIT_WRITE, expr.span, - &format!("use of `{used}.unwrap()`"), + format!("use of `{used}.unwrap()`"), "try", format!("{prefix}{sugg_mac}!({inputs_snippet})"), applicability, diff --git a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs index 538d29eb43dca..7484f772e0823 100644 --- a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs +++ b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs @@ -110,11 +110,11 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> { .map_or(param.span, |bound_span| param.span.with_hi(bound_span.hi())) } - fn emit_help(&self, spans: Vec, msg: &str, help: &'static str) { + fn emit_help(&self, spans: Vec, msg: String, help: &'static str) { span_lint_and_help(self.cx, EXTRA_UNUSED_TYPE_PARAMETERS, spans, msg, None, help); } - fn emit_sugg(&self, spans: Vec, msg: &str, help: &'static str) { + fn emit_sugg(&self, spans: Vec, msg: String, help: &'static str) { let suggestions: Vec<(Span, String)> = spans.iter().copied().zip(std::iter::repeat(String::new())).collect(); span_lint_and_then(self.cx, EXTRA_UNUSED_TYPE_PARAMETERS, spans, msg, |diag| { diag.multipart_suggestion(help, suggestions, Applicability::MachineApplicable); @@ -167,7 +167,7 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> { .iter() .map(|(_, param)| self.get_bound_span(param)) .collect::>(); - self.emit_help(spans, &msg, help); + self.emit_help(spans, msg, help); } else { let spans = if explicit_params.len() == extra_params.len() { vec![self.generics.span] // Remove the entire list of generics @@ -196,7 +196,7 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> { }) .collect() }; - self.emit_sugg(spans, &msg, help); + self.emit_sugg(spans, msg, help); }; } } diff --git a/src/tools/clippy/clippy_lints/src/float_literal.rs b/src/tools/clippy/clippy_lints/src/float_literal.rs index 981a76d683d2e..2cd4e9e99a56b 100644 --- a/src/tools/clippy/clippy_lints/src/float_literal.rs +++ b/src/tools/clippy/clippy_lints/src/float_literal.rs @@ -83,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral { LitFloatType::Unsuffixed => None, }; let (is_whole, is_inf, mut float_str) = match fty { - FloatTy::F16 => { + FloatTy::F16 | FloatTy::F128 => { // FIXME(f16_f128): do a check like the others when parsing is available return; }, @@ -97,10 +97,6 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral { (value.fract() == 0.0, value.is_infinite(), formatter.format(value)) }, - FloatTy::F128 => { - // FIXME(f16_f128): do a check like the others when parsing is available - return; - }, }; if is_inf { diff --git a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs index c8b87e510ed68..46d47e217b04a 100644 --- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs @@ -552,9 +552,9 @@ fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) - /// Returns true iff expr is some zero literal fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match constant_simple(cx, cx.typeck_results(), expr) { - Some(Constant::Int(i)) => i == 0, - Some(Constant::F32(f)) => f == 0.0, - Some(Constant::F64(f)) => f == 0.0, + Some(Int(i)) => i == 0, + Some(F32(f)) => f == 0.0, + Some(F64(f)) => f == 0.0, _ => false, } } diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs index 61f550ce0beb4..80db617c639a5 100644 --- a/src/tools/clippy/clippy_lints/src/format_args.rs +++ b/src/tools/clippy/clippy_lints/src/format_args.rs @@ -401,7 +401,7 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> { self.cx, FORMAT_IN_FORMAT_ARGS, self.macro_call.span, - &format!("`format!` in `{name}!` args"), + format!("`format!` in `{name}!` args"), |diag| { diag.help(format!( "combine the `format!(..)` arguments with the outer `{name}!(..)` call" @@ -431,7 +431,7 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> { cx, TO_STRING_IN_FORMAT_ARGS, to_string_span.with_lo(receiver.span.hi()), - &format!("`to_string` applied to a type that implements `Display` in `{name}!` args"), + format!("`to_string` applied to a type that implements `Display` in `{name}!` args"), "remove this", String::new(), Applicability::MachineApplicable, @@ -441,7 +441,7 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> { cx, TO_STRING_IN_FORMAT_ARGS, value.span, - &format!("`to_string` applied to a type that implements `Display` in `{name}!` args"), + format!("`to_string` applied to a type that implements `Display` in `{name}!` args"), "use this", format!( "{}{:*>n_needed_derefs$}{receiver_snippet}", diff --git a/src/tools/clippy/clippy_lints/src/format_impl.rs b/src/tools/clippy/clippy_lints/src/format_impl.rs index 93517076cda01..0a52347940abb 100644 --- a/src/tools/clippy/clippy_lints/src/format_impl.rs +++ b/src/tools/clippy/clippy_lints/src/format_impl.rs @@ -214,7 +214,7 @@ impl<'a, 'tcx> FormatImplExpr<'a, 'tcx> { self.cx, RECURSIVE_FORMAT_IMPL, self.expr.span, - &format!("using `self` as `{name}` in `impl {name}` will cause infinite recursion"), + format!("using `self` as `{name}` in `impl {name}` will cause infinite recursion"), ); } } @@ -235,7 +235,7 @@ impl<'a, 'tcx> FormatImplExpr<'a, 'tcx> { self.cx, PRINT_IN_FORMAT_IMPL, macro_call.span, - &format!("use of `{name}!` in `{}` impl", self.format_trait_impl.name), + format!("use of `{name}!` in `{}` impl", self.format_trait_impl.name), "replace with", if let Some(formatter_name) = self.format_trait_impl.formatter_name { format!("{replacement}!({formatter_name}, ..)") diff --git a/src/tools/clippy/clippy_lints/src/formatting.rs b/src/tools/clippy/clippy_lints/src/formatting.rs index c3ef6f180c9fa..34e93bdb9b9b0 100644 --- a/src/tools/clippy/clippy_lints/src/formatting.rs +++ b/src/tools/clippy/clippy_lints/src/formatting.rs @@ -151,12 +151,12 @@ fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) { cx, SUSPICIOUS_ASSIGNMENT_FORMATTING, eqop_span, - &format!( + format!( "this looks like you are trying to use `.. {op}= ..`, but you \ really are doing `.. = ({op} ..)`" ), None, - &format!("to remove this lint, use either `{op}=` or `= {op}`"), + format!("to remove this lint, use either `{op}=` or `= {op}`"), ); } } @@ -187,12 +187,12 @@ fn check_unop(cx: &EarlyContext<'_>, expr: &Expr) { cx, SUSPICIOUS_UNARY_OP_FORMATTING, eqop_span, - &format!( + format!( "by not having a space between `{binop_str}` and `{unop_str}` it looks like \ `{binop_str}{unop_str}` is a single operator" ), None, - &format!("put a space between `{binop_str}` and `{unop_str}` and remove the space after `{unop_str}`"), + format!("put a space between `{binop_str}` and `{unop_str}` and remove the space after `{unop_str}`"), ); } } @@ -215,6 +215,7 @@ fn check_else(cx: &EarlyContext<'_>, expr: &Expr) { // it’s bad when there is a ‘\n’ after the “else” && let Some(else_snippet) = snippet_opt(cx, else_span) && let Some((pre_else, post_else)) = else_snippet.split_once("else") + && !else_snippet.contains('/') && let Some((_, post_else_post_eol)) = post_else.split_once('\n') { // Allow allman style braces `} \n else \n {` @@ -238,9 +239,9 @@ fn check_else(cx: &EarlyContext<'_>, expr: &Expr) { cx, SUSPICIOUS_ELSE_FORMATTING, else_span, - &format!("this is an `else {else_desc}` but the formatting might hide it"), + format!("this is an `else {else_desc}` but the formatting might hide it"), None, - &format!( + format!( "to remove this lint, remove the `else` or remove the new line between \ `else` and `{else_desc}`", ), @@ -308,9 +309,9 @@ fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) { cx, SUSPICIOUS_ELSE_FORMATTING, else_span, - &format!("this looks like {looks_like} but the `else` is missing"), + format!("this looks like {looks_like} but the `else` is missing"), None, - &format!("to remove this lint, add the missing `else` or add a new line before {next_thing}",), + format!("to remove this lint, add the missing `else` or add a new line before {next_thing}",), ); } } diff --git a/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs b/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs index 286ba2306c922..ba2495c17a21a 100644 --- a/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs @@ -4,7 +4,7 @@ use clippy_utils::ty::is_c_void; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{RawPtr}; +use rustc_middle::ty::RawPtr; use rustc_session::declare_lint_pass; use rustc_span::sym; @@ -52,7 +52,7 @@ impl LateLintPass<'_> for FromRawWithVoidPtr { cx, FROM_RAW_WITH_VOID_PTR, expr.span, - &msg, + msg, Some(arg.span), "cast this to a pointer of the appropriate type", ); diff --git a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs index bf96c0d62b05a..8ac17e17688d3 100644 --- a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs +++ b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs @@ -24,13 +24,13 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body: let name = ident.name.as_str(); let name = match decl.implicit_self { - ImplicitSelfKind::MutRef => { + ImplicitSelfKind::RefMut => { let Some(name) = name.strip_suffix("_mut") else { return; }; name }, - ImplicitSelfKind::Imm | ImplicitSelfKind::Mut | ImplicitSelfKind::ImmRef => name, + ImplicitSelfKind::Imm | ImplicitSelfKind::Mut | ImplicitSelfKind::RefImm => name, ImplicitSelfKind::None => return, }; diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs index d752d010f9fe4..d0c66900c0068 100644 --- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs +++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs @@ -142,7 +142,7 @@ fn check_must_use_candidate<'tcx>( item_span: Span, item_id: hir::OwnerId, fn_span: Span, - msg: &str, + msg: &'static str, ) { if has_mutable_arg(cx, body) || mutates_static(cx, body) @@ -207,9 +207,7 @@ fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, tys: &mut DefIdSet) }, ty::Tuple(args) => args.iter().any(|ty| is_mutable_ty(cx, ty, tys)), ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, tys), - ty::RawPtr(ty, mutbl) | ty::Ref(_, ty, mutbl) => { - mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, tys) - }, + ty::RawPtr(ty, mutbl) | ty::Ref(_, ty, mutbl) => mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, tys), // calling something constitutes a side effect, so return true on all callables // also never calls need not be used, so return true for them, too _ => true, diff --git a/src/tools/clippy/clippy_lints/src/functions/result.rs b/src/tools/clippy/clippy_lints/src/functions/result.rs index 37fbf2c7d5960..93f088d3e3392 100644 --- a/src/tools/clippy/clippy_lints/src/functions/result.rs +++ b/src/tools/clippy/clippy_lints/src/functions/result.rs @@ -2,7 +2,7 @@ use rustc_errors::Diag; use rustc_hir as hir; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Adt, Ty}; +use rustc_middle::ty::{Adt, Ty}; use rustc_span::{sym, Span}; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; @@ -25,7 +25,7 @@ fn result_err_ty<'tcx>( .tcx .instantiate_bound_regions_with_erased(cx.tcx.fn_sig(id).instantiate_identity().output()) && is_type_diagnostic_item(cx, ty, sym::Result) - && let ty::Adt(_, args) = ty.kind() + && let Adt(_, args) = ty.kind() { let err_ty = args.type_at(1); Some((hir_ty, err_ty)) diff --git a/src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs b/src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs index 1e08922a61664..e72a2ad49d826 100644 --- a/src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs +++ b/src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs @@ -59,7 +59,7 @@ fn check_arg_number(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, fn_span: Span, cx, TOO_MANY_ARGUMENTS, fn_span, - &format!("this function has too many arguments ({args}/{too_many_arguments_threshold})"), + format!("this function has too many arguments ({args}/{too_many_arguments_threshold})"), ); } } diff --git a/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs b/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs index 34f1bf3b2b1d6..586ca58d60dd6 100644 --- a/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs +++ b/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs @@ -77,7 +77,7 @@ pub(super) fn check_fn( cx, TOO_MANY_LINES, span, - &format!("this function has too many lines ({line_count}/{too_many_lines_threshold})"), + format!("this function has too many lines ({line_count}/{too_many_lines_threshold})"), ); } } diff --git a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs index 8f48941c4a91e..f5ba62ae432e8 100644 --- a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs +++ b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs @@ -117,9 +117,9 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { cx, IF_THEN_SOME_ELSE_NONE, expr.span, - &format!("this could be simplified with `bool::{method_name}`"), + format!("this could be simplified with `bool::{method_name}`"), None, - &help, + help, ); } } diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs index 8acb138332cfb..a46aae36d5c5c 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs @@ -141,7 +141,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { cx, IMPLICIT_HASHER, target.span(), - &format!( + format!( "impl for `{}` should be generalized over different hashers", target.type_name() ), @@ -187,7 +187,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { cx, IMPLICIT_HASHER, target.span(), - &format!( + format!( "parameter of type `{}` should be generalized over different hashers", target.type_name() ), 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 9f4d7b51271b0..7b97fc15caaf8 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 @@ -56,7 +56,7 @@ fn emit_lint( index: usize, // The bindings that were implied, used for suggestion purposes since removing a bound with associated types // means we might need to then move it to a different bound - implied_bindings: &[rustc_hir::TypeBinding<'_>], + implied_bindings: &[TypeBinding<'_>], bound: &ImplTraitBound<'_>, ) { let implied_by = snippet(cx, bound.span, ".."); @@ -65,7 +65,7 @@ fn emit_lint( cx, IMPLIED_BOUNDS_IN_IMPLS, poly_trait.span, - &format!("this bound is already specified as the supertrait of `{implied_by}`"), + format!("this bound is already specified as the supertrait of `{implied_by}`"), |diag| { // If we suggest removing a bound, we may also need to extend the span // to include the `+` token that is ahead or behind, diff --git a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs index cd000fcd18449..35b4481bfee7e 100644 --- a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs +++ b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs @@ -33,7 +33,7 @@ declare_clippy_lint! { /// /// To fix this problem, either increase your MSRV or use another item /// available in your current MSRV. - #[clippy::version = "1.77.0"] + #[clippy::version = "1.78.0"] pub INCOMPATIBLE_MSRV, suspicious, "ensures that all items used in the crate are available for the current MSRV" @@ -104,7 +104,7 @@ impl IncompatibleMsrv { cx, INCOMPATIBLE_MSRV, span, - &format!( + format!( "current MSRV (Minimum Supported Rust Version) is `{}` but this item is stable since `{version}`", self.msrv ), diff --git a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs index 5b5eb355f86cd..4d1f89b1d9d92 100644 --- a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs +++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs @@ -97,7 +97,7 @@ fn find_slice_values(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> FxIndexMap, pat: &hir::Pat<'_>) -> FxIndexMap, item: &ImplItem<'_>) { cx, INHERENT_TO_STRING_SHADOW_DISPLAY, item.span, - &format!( + format!( "type `{self_type}` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display`" ), None, - &format!("remove the inherent method from type `{self_type}`"), + format!("remove the inherent method from type `{self_type}`"), ); } else { span_lint_and_help( cx, INHERENT_TO_STRING, item.span, - &format!("implementation of inherent method `to_string(&self) -> String` for type `{self_type}`"), + format!("implementation of inherent method `to_string(&self) -> String` for type `{self_type}`"), None, - &format!("implement trait `Display` for type `{self_type}` instead"), + format!("implement trait `Display` for type `{self_type}` instead"), ); } } diff --git a/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs b/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs index 83ecaeef98257..860258fd030e0 100644 --- a/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs +++ b/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs @@ -51,7 +51,7 @@ fn check_attrs(cx: &LateContext<'_>, name: Symbol, attrs: &[Attribute]) { cx, INLINE_FN_WITHOUT_BODY, attr.span, - &format!("use of `#[inline]` on trait method `{name}` which has no body"), + format!("use of `#[inline]` on trait method `{name}` which has no body"), |diag| { diag.suggest_remove_item(cx, attr.span, "remove", Applicability::MachineApplicable); }, diff --git a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs index 17b6256f982b2..10b00f632bb00 100644 --- a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs +++ b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs @@ -1,5 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; -use clippy_utils::diagnostics::{self, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::Sugg; use clippy_utils::ty; @@ -149,7 +149,7 @@ fn print_unchecked_duration_subtraction_sugg( let left_expr = snippet_with_context(cx, left_expr.span, ctxt, "", &mut applicability).0; let right_expr = snippet_with_context(cx, right_expr.span, ctxt, "", &mut applicability).0; - diagnostics::span_lint_and_sugg( + span_lint_and_sugg( cx, UNCHECKED_DURATION_SUBTRACTION, expr.span, diff --git a/src/tools/clippy/clippy_lints/src/integer_division_remainder_used.rs b/src/tools/clippy/clippy_lints/src/integer_division_remainder_used.rs index 36dc45ca788da..a3577b765c03f 100644 --- a/src/tools/clippy/clippy_lints/src/integer_division_remainder_used.rs +++ b/src/tools/clippy/clippy_lints/src/integer_division_remainder_used.rs @@ -43,7 +43,7 @@ impl LateLintPass<'_> for IntegerDivisionRemainderUsed { cx, INTEGER_DIVISION_REMAINDER_USED, expr.span.source_callsite(), - &format!("use of {} has been disallowed in this context", op.node.as_str()), + format!("use of {} has been disallowed in this context", op.node.as_str()), ); } } diff --git a/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs b/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs index 8bcd9b532bd10..30f2285bdd235 100644 --- a/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs @@ -76,7 +76,7 @@ fn err_upcast_comparison(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, alwa cx, INVALID_UPCAST_COMPARISONS, span, - &format!( + format!( "because of the numeric bounds on `{}` prior to casting, this expression is always {}", snippet(cx, cast_val.span, "the expression"), if always { "true" } else { "false" }, @@ -88,7 +88,7 @@ fn err_upcast_comparison(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, alwa fn upcast_comparison_bounds_err<'tcx>( cx: &LateContext<'tcx>, span: Span, - rel: comparisons::Rel, + rel: Rel, lhs_bounds: Option<(FullInt, FullInt)>, lhs: &'tcx Expr<'_>, rhs: &'tcx Expr<'_>, diff --git a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs index 0b4c416d94db0..6615122567dc5 100644 --- a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs +++ b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs @@ -240,9 +240,9 @@ fn check_fields(cx: &LateContext<'_>, threshold: u64, item: &Item<'_>, fields: & cx, STRUCT_FIELD_NAMES, item.span, - &format!("all fields have the same {what}fix: `{value}`"), + format!("all fields have the same {what}fix: `{value}`"), None, - &format!("remove the {what}fixes"), + format!("remove the {what}fixes"), ); } } @@ -370,9 +370,9 @@ fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_n cx, ENUM_VARIANT_NAMES, span, - &format!("all variants have the same {what}fix: `{value}`"), + format!("all variants have the same {what}fix: `{value}`"), None, - &format!( + format!( "remove the {what}fixes and use full paths to \ the variants instead of glob imports" ), diff --git a/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs b/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs index 32ae6be568735..1b5f1b499475f 100644 --- a/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs @@ -84,7 +84,7 @@ fn check_sig(cx: &LateContext<'_>, name: &str, sig: &FnSig<'_>, fn_id: LocalDefI cx, ITER_NOT_RETURNING_ITERATOR, sig.span, - &format!("this method is named `{name}` but its return type does not implement `Iterator`"), + format!("this method is named `{name}` but its return type does not implement `Iterator`"), ); } } diff --git a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs index b5821d909f849..c749a71233033 100644 --- a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs +++ b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs @@ -182,7 +182,7 @@ impl LateLintPass<'_> for IterWithoutIntoIter { cx, INTO_ITER_WITHOUT_ITER, item.span, - &format!("`IntoIterator` implemented for a reference type without an `{expected_method_name}` method"), + format!("`IntoIterator` implemented for a reference type without an `{expected_method_name}` method"), |diag| { // The suggestion forwards to the `IntoIterator` impl and uses a form of UFCS // to avoid name ambiguities, as there might be an inherent into_iter method @@ -216,8 +216,8 @@ impl {self_ty_without_ref} {{ fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::ImplItem<'_>) { let item_did = item.owner_id.to_def_id(); let (borrow_prefix, expected_implicit_self) = match item.ident.name { - sym::iter => ("&", ImplicitSelfKind::ImmRef), - sym::iter_mut => ("&mut ", ImplicitSelfKind::MutRef), + sym::iter => ("&", ImplicitSelfKind::RefImm), + sym::iter_mut => ("&mut ", ImplicitSelfKind::RefMut), _ => return, }; @@ -258,7 +258,7 @@ impl {self_ty_without_ref} {{ cx, ITER_WITHOUT_INTO_ITER, item.span, - &format!( + format!( "`{}` method without an `IntoIterator` impl for `{self_ty_snippet}`", item.ident ), diff --git a/src/tools/clippy/clippy_lints/src/large_futures.rs b/src/tools/clippy/clippy_lints/src/large_futures.rs index eb7570e9b44eb..07488a512a37e 100644 --- a/src/tools/clippy/clippy_lints/src/large_futures.rs +++ b/src/tools/clippy/clippy_lints/src/large_futures.rs @@ -71,7 +71,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeFuture { cx, LARGE_FUTURES, expr.span, - &format!("large future with a size of {} bytes", size.bytes()), + format!("large future with a size of {} bytes", size.bytes()), "consider `Box::pin` on it", format!("Box::pin({})", snippet(cx, expr.span, "..")), Applicability::Unspecified, diff --git a/src/tools/clippy/clippy_lints/src/large_include_file.rs b/src/tools/clippy/clippy_lints/src/large_include_file.rs index 1b5981ecc281a..0599afca09f88 100644 --- a/src/tools/clippy/clippy_lints/src/large_include_file.rs +++ b/src/tools/clippy/clippy_lints/src/large_include_file.rs @@ -74,7 +74,7 @@ impl LateLintPass<'_> for LargeIncludeFile { expr.span, "attempted to include a large file", None, - &format!( + format!( "the configuration allows a maximum size of {} bytes", self.max_file_size ), diff --git a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs index fd33ba91bfd76..afcb674594767 100644 --- a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs +++ b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs @@ -58,12 +58,12 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays { cx, LARGE_STACK_ARRAYS, expr.span, - &format!( + format!( "allocating a local array larger than {} bytes", self.maximum_allowed_size ), None, - &format!( + format!( "consider allocating on the heap with `vec!{}.into_boxed_slice()`", snippet(cx, expr.span, "[...]") ), diff --git a/src/tools/clippy/clippy_lints/src/large_stack_frames.rs b/src/tools/clippy/clippy_lints/src/large_stack_frames.rs index b397180a69c55..49408d7e24354 100644 --- a/src/tools/clippy/clippy_lints/src/large_stack_frames.rs +++ b/src/tools/clippy/clippy_lints/src/large_stack_frames.rs @@ -1,10 +1,12 @@ -use std::ops::AddAssign; +use std::{fmt, ops}; -use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::fn_has_unsatisfiable_preds; +use clippy_utils::source::snippet_opt; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl}; +use rustc_lexer::is_ident; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::Span; @@ -108,13 +110,25 @@ impl Space { } } -impl AddAssign for Space { - fn add_assign(&mut self, rhs: u64) { - if let Self::Used(lhs) = self { - match lhs.checked_add(rhs) { - Some(sum) => *self = Self::Used(sum), - None => *self = Self::Overflow, - } +impl fmt::Display for Space { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Space::Used(1) => write!(f, "1 byte"), + Space::Used(n) => write!(f, "{n} bytes"), + Space::Overflow => write!(f, "over 2⁶⁴-1 bytes"), + } + } +} + +impl ops::Add for Space { + type Output = Self; + fn add(self, rhs: u64) -> Self { + match self { + Self::Used(lhs) => match lhs.checked_add(rhs) { + Some(sum) => Self::Used(sum), + None => Self::Overflow, + }, + Self::Overflow => self, } } } @@ -123,10 +137,10 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackFrames { fn check_fn( &mut self, cx: &LateContext<'tcx>, - _: FnKind<'tcx>, + fn_kind: FnKind<'tcx>, _: &'tcx FnDecl<'tcx>, _: &'tcx Body<'tcx>, - span: Span, + entire_fn_span: Span, local_def_id: LocalDefId, ) { let def_id = local_def_id.to_def_id(); @@ -138,22 +152,68 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackFrames { let mir = cx.tcx.optimized_mir(def_id); let param_env = cx.tcx.param_env(def_id); - let mut frame_size = Space::Used(0); + let sizes_of_locals = || { + mir.local_decls.iter().filter_map(|local| { + let layout = cx.tcx.layout_of(param_env.and(local.ty)).ok()?; + Some((local, layout.size.bytes())) + }) + }; - for local in &mir.local_decls { - if let Ok(layout) = cx.tcx.layout_of(param_env.and(local.ty)) { - frame_size += layout.size.bytes(); - } - } + let frame_size = sizes_of_locals().fold(Space::Used(0), |sum, (_, size)| sum + size); + + let limit = self.maximum_allowed_size; + if frame_size.exceeds_limit(limit) { + // Point at just the function name if possible, because lints that span + // the entire body and don't have to are less legible. + let fn_span = match fn_kind { + FnKind::ItemFn(ident, _, _) | FnKind::Method(ident, _) => ident.span, + FnKind::Closure => entire_fn_span, + }; - if frame_size.exceeds_limit(self.maximum_allowed_size) { - span_lint_and_note( + span_lint_and_then( cx, LARGE_STACK_FRAMES, - span, - "this function allocates a large amount of stack space", - None, - "allocating large amounts of stack space can overflow the stack", + fn_span, + format!("this function may allocate {frame_size} on the stack"), + |diag| { + // Point out the largest individual contribution to this size, because + // it is the most likely to be unintentionally large. + if let Some((local, size)) = sizes_of_locals().max_by_key(|&(_, size)| size) { + let local_span: Span = local.source_info.span; + let size = Space::Used(size); // pluralizes for us + let ty = local.ty; + + // TODO: Is there a cleaner, robust way to ask this question? + // The obvious `LocalDecl::is_user_variable()` panics on "unwrapping cross-crate data", + // and that doesn't get us the true name in scope rather than the span text either. + if let Some(name) = snippet_opt(cx, local_span) + && is_ident(&name) + { + // If the local is an ordinary named variable, + // print its name rather than relying solely on the span. + diag.span_label( + local_span, + format!("`{name}` is the largest part, at {size} for type `{ty}`"), + ); + } else { + diag.span_label( + local_span, + format!("this is the largest part, at {size} for type `{ty}`"), + ); + } + } + + // Explain why we are linting this and not other functions. + diag.note(format!( + "{frame_size} is larger than Clippy's configured `stack-size-threshold` of {limit}" + )); + + // Explain why the user should care, briefly. + diag.note_once( + "allocating large amounts of stack space can overflow the stack \ + and cause the program to abort", + ); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs new file mode 100644 index 0000000000000..c5f1afe68c3f4 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs @@ -0,0 +1,293 @@ +use clippy_config::msrvs::{Msrv, NUMERIC_ASSOCIATED_CONSTANTS}; +use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; +use clippy_utils::{get_parent_expr, is_from_proc_macro}; +use hir::def_id::DefId; +use rustc_errors::{Applicability, SuggestionStyle}; +use rustc_hir as hir; +use rustc_hir::{ExprKind, Item, ItemKind, QPath, UseKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::impl_lint_pass; +use rustc_span::symbol::kw; +use rustc_span::{sym, Symbol}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of `::max_value()`, `std::::MAX`, + /// `std::::EPSILON`, etc. + /// + /// ### Why is this bad? + /// All of these have been superceded by the associated constants on their respective types, + /// such as `i128::MAX`. These legacy items may be deprecated in a future version of rust. + /// + /// ### Example + /// ```rust + /// let eps = std::f32::EPSILON; + /// ``` + /// Use instead: + /// ```rust + /// let eps = f32::EPSILON; + /// ``` + #[clippy::version = "1.72.0"] + pub LEGACY_NUMERIC_CONSTANTS, + style, + "checks for usage of legacy std numeric constants and methods" +} +pub struct LegacyNumericConstants { + msrv: Msrv, +} + +impl LegacyNumericConstants { + #[must_use] + pub fn new(msrv: Msrv) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(LegacyNumericConstants => [LEGACY_NUMERIC_CONSTANTS]); + +impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + let Self { msrv } = self; + + if !msrv.meets(NUMERIC_ASSOCIATED_CONSTANTS) || in_external_macro(cx.sess(), item.span) { + return; + } + + // Integer modules are "TBD" deprecated, and the contents are too, + // so lint on the `use` statement directly. + if let ItemKind::Use(path, kind @ (UseKind::Single | UseKind::Glob)) = item.kind + && let Some(def_id) = path.res[0].opt_def_id() + { + let module = if is_integer_module(cx, def_id) { + true + } else if is_numeric_const(cx, def_id) { + false + } else { + return; + }; + + span_lint_and_then( + cx, + LEGACY_NUMERIC_CONSTANTS, + path.span, + if module { + "importing legacy numeric constants" + } else { + "importing a legacy numeric constant" + }, + |diag| { + if item.ident.name == kw::Underscore { + diag.help("remove this import"); + return; + } + + let def_path = cx.get_def_path(def_id); + + if module && let [.., module_name] = &*def_path { + if kind == UseKind::Glob { + diag.help(format!("remove this import and use associated constants `{module_name}::` from the primitive type instead")); + } else { + diag.help("remove this import").note(format!( + "then `{module_name}::` will resolve to the respective associated constant" + )); + } + } else if let [.., module_name, name] = &*def_path { + diag.help( + format!("remove this import and use the associated constant `{module_name}::{name}` from the primitive type instead") + ); + } + }, + ); + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { + let Self { msrv } = self; + + if !msrv.meets(NUMERIC_ASSOCIATED_CONSTANTS) || in_external_macro(cx.sess(), expr.span) { + return; + } + let ExprKind::Path(qpath) = expr.kind else { + return; + }; + + // `std::::` check + let (span, sugg, msg) = if let QPath::Resolved(None, path) = qpath + && let Some(def_id) = path.res.opt_def_id() + && is_numeric_const(cx, def_id) + && let def_path = cx.get_def_path(def_id) + && let [.., mod_name, name] = &*def_path + // Skip linting if this usage looks identical to the associated constant, + // since this would only require removing a `use` import (which is already linted). + && !is_numeric_const_path_canonical(path, [*mod_name, *name]) + { + ( + expr.span, + format!("{mod_name}::{name}"), + "usage of a legacy numeric constant", + ) + // `::xxx_value` check + } else if let QPath::TypeRelative(_, last_segment) = qpath + && let Some(def_id) = cx.qpath_res(&qpath, expr.hir_id).opt_def_id() + && is_integer_method(cx, def_id) + && let Some(par_expr) = get_parent_expr(cx, expr) + && let ExprKind::Call(_, _) = par_expr.kind + { + let name = last_segment.ident.name.as_str(); + + ( + last_segment.ident.span.with_hi(par_expr.span.hi()), + name[..=2].to_ascii_uppercase(), + "usage of a legacy numeric method", + ) + } else { + return; + }; + + if is_from_proc_macro(cx, expr) { + return; + } + + span_lint_hir_and_then(cx, LEGACY_NUMERIC_CONSTANTS, expr.hir_id, span, msg, |diag| { + diag.span_suggestion_with_style( + span, + "use the associated constant instead", + sugg, + Applicability::MaybeIncorrect, + SuggestionStyle::ShowAlways, + ); + }); + } + + extract_msrv_attr!(LateContext); +} + +fn is_integer_module(cx: &LateContext<'_>, did: DefId) -> bool { + matches!( + cx.tcx.get_diagnostic_name(did), + Some( + sym::isize_legacy_mod + | sym::i128_legacy_mod + | sym::i64_legacy_mod + | sym::i32_legacy_mod + | sym::i16_legacy_mod + | sym::i8_legacy_mod + | sym::usize_legacy_mod + | sym::u128_legacy_mod + | sym::u64_legacy_mod + | sym::u32_legacy_mod + | sym::u16_legacy_mod + | sym::u8_legacy_mod + ) + ) +} + +fn is_numeric_const(cx: &LateContext<'_>, did: DefId) -> bool { + matches!( + cx.tcx.get_diagnostic_name(did), + Some( + sym::isize_legacy_const_max + | sym::isize_legacy_const_min + | sym::i128_legacy_const_max + | sym::i128_legacy_const_min + | sym::i16_legacy_const_max + | sym::i16_legacy_const_min + | sym::i32_legacy_const_max + | sym::i32_legacy_const_min + | sym::i64_legacy_const_max + | sym::i64_legacy_const_min + | sym::i8_legacy_const_max + | sym::i8_legacy_const_min + | sym::usize_legacy_const_max + | sym::usize_legacy_const_min + | sym::u128_legacy_const_max + | sym::u128_legacy_const_min + | sym::u16_legacy_const_max + | sym::u16_legacy_const_min + | sym::u32_legacy_const_max + | sym::u32_legacy_const_min + | sym::u64_legacy_const_max + | sym::u64_legacy_const_min + | sym::u8_legacy_const_max + | sym::u8_legacy_const_min + | sym::f32_legacy_const_digits + | sym::f32_legacy_const_epsilon + | sym::f32_legacy_const_infinity + | sym::f32_legacy_const_mantissa_dig + | sym::f32_legacy_const_max + | sym::f32_legacy_const_max_10_exp + | sym::f32_legacy_const_max_exp + | sym::f32_legacy_const_min + | sym::f32_legacy_const_min_10_exp + | sym::f32_legacy_const_min_exp + | sym::f32_legacy_const_min_positive + | sym::f32_legacy_const_nan + | sym::f32_legacy_const_neg_infinity + | sym::f32_legacy_const_radix + | sym::f64_legacy_const_digits + | sym::f64_legacy_const_epsilon + | sym::f64_legacy_const_infinity + | sym::f64_legacy_const_mantissa_dig + | sym::f64_legacy_const_max + | sym::f64_legacy_const_max_10_exp + | sym::f64_legacy_const_max_exp + | sym::f64_legacy_const_min + | sym::f64_legacy_const_min_10_exp + | sym::f64_legacy_const_min_exp + | sym::f64_legacy_const_min_positive + | sym::f64_legacy_const_nan + | sym::f64_legacy_const_neg_infinity + | sym::f64_legacy_const_radix + ) + ) +} + +// Whether path expression looks like `i32::MAX` +fn is_numeric_const_path_canonical(expr_path: &hir::Path<'_>, [mod_name, name]: [Symbol; 2]) -> bool { + let [ + hir::PathSegment { + ident: one, args: None, .. + }, + hir::PathSegment { + ident: two, args: None, .. + }, + ] = expr_path.segments + else { + return false; + }; + + one.name == mod_name && two.name == name +} + +fn is_integer_method(cx: &LateContext<'_>, did: DefId) -> bool { + matches!( + cx.tcx.get_diagnostic_name(did), + Some( + sym::isize_legacy_fn_max_value + | sym::isize_legacy_fn_min_value + | sym::i128_legacy_fn_max_value + | sym::i128_legacy_fn_min_value + | sym::i16_legacy_fn_max_value + | sym::i16_legacy_fn_min_value + | sym::i32_legacy_fn_max_value + | sym::i32_legacy_fn_min_value + | sym::i64_legacy_fn_max_value + | sym::i64_legacy_fn_min_value + | sym::i8_legacy_fn_max_value + | sym::i8_legacy_fn_min_value + | sym::usize_legacy_fn_max_value + | sym::usize_legacy_fn_min_value + | sym::u128_legacy_fn_max_value + | sym::u128_legacy_fn_min_value + | sym::u16_legacy_fn_max_value + | sym::u16_legacy_fn_min_value + | sym::u32_legacy_fn_max_value + | sym::u32_legacy_fn_min_value + | sym::u64_legacy_fn_max_value + | sym::u64_legacy_fn_min_value + | sym::u8_legacy_fn_max_value + | sym::u8_legacy_fn_min_value + ) + ) +} diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 27d85cde5320a..97a245b76d445 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::snippet_with_context; -use clippy_utils::sugg::Sugg; +use clippy_utils::source::{snippet_opt, snippet_with_context}; +use clippy_utils::sugg::{has_enclosing_paren, Sugg}; use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -192,7 +192,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { if let ExprKind::Binary(Spanned { node: cmp, .. }, left, right) = expr.kind { // expr.span might contains parenthesis, see issue #10529 - let actual_span = left.span.with_hi(right.span.hi()); + let actual_span = span_without_enclosing_paren(cx, expr.span); match cmp { BinOpKind::Eq => { check_cmp(cx, actual_span, left, right, "", 0); // len == 0 @@ -218,6 +218,20 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { } } +fn span_without_enclosing_paren(cx: &LateContext<'_>, span: Span) -> Span { + let Some(snippet) = snippet_opt(cx, span) else { + return span; + }; + if has_enclosing_paren(snippet) { + let source_map = cx.tcx.sess.source_map(); + let left_paren = source_map.start_point(span); + let right_parent = source_map.end_point(span); + left_paren.between(right_parent) + } else { + span + } +} + fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items: &[TraitItemRef]) { fn is_named_self(cx: &LateContext<'_>, item: &TraitItemRef, name: Symbol) -> bool { item.ident.name == name @@ -256,7 +270,7 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items .items() .flat_map(|&i| cx.tcx.associated_items(i).filter_by_name_unhygienic(is_empty)) .any(|i| { - i.kind == ty::AssocKind::Fn + i.kind == AssocKind::Fn && i.fn_has_self_parameter && cx.tcx.fn_sig(i.def_id).skip_binder().inputs().skip_binder().len() == 1 }); @@ -266,7 +280,7 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items cx, LEN_WITHOUT_IS_EMPTY, visited_trait.span, - &format!( + format!( "trait `{}` has a `len` method but no (possibly inherited) `is_empty` method", visited_trait.ident.name ), @@ -384,8 +398,8 @@ impl LenOutput { fn expected_sig(self, self_kind: ImplicitSelfKind) -> String { let self_ref = match self_kind { - ImplicitSelfKind::ImmRef => "&", - ImplicitSelfKind::MutRef => "&mut ", + ImplicitSelfKind::RefImm => "&", + ImplicitSelfKind::RefMut => "&mut ", _ => "", }; match self { @@ -411,8 +425,8 @@ fn check_is_empty_sig<'tcx>( [arg, res] if len_output.matches_is_empty_output(cx, *res) => { matches!( (arg.kind(), self_kind), - (ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::ImmRef) - | (ty::Ref(_, _, Mutability::Mut), ImplicitSelfKind::MutRef) + (ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::RefImm) + | (ty::Ref(_, _, Mutability::Mut), ImplicitSelfKind::RefMut) ) || (!arg.is_ref() && matches!(self_kind, ImplicitSelfKind::Imm | ImplicitSelfKind::Mut)) }, _ => false, @@ -484,7 +498,7 @@ fn check_for_is_empty( Some(_) => return, }; - span_lint_and_then(cx, LEN_WITHOUT_IS_EMPTY, span, &msg, |db| { + span_lint_and_then(cx, LEN_WITHOUT_IS_EMPTY, span, msg, |db| { if let Some(span) = is_empty_span { db.span_note(span, "`is_empty` defined here"); } @@ -495,6 +509,10 @@ fn check_for_is_empty( } fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) { + if method.span.from_expansion() { + return; + } + if let (&ExprKind::MethodCall(method_path, receiver, args, _), ExprKind::Lit(lit)) = (&method.kind, &lit.kind) { // check if we are in an is_empty() method if let Some(name) = get_item_name(cx, method) { @@ -542,8 +560,8 @@ fn check_len( cx, LEN_ZERO, span, - &format!("length comparison to {}", if compare_to == 0 { "zero" } else { "one" }), - &format!("using `{op}is_empty` is clearer and more explicit"), + format!("length comparison to {}", if compare_to == 0 { "zero" } else { "one" }), + format!("using `{op}is_empty` is clearer and more explicit"), format!( "{op}{}.is_empty()", snippet_with_context(cx, receiver.span, span.ctxt(), "_", &mut applicability).0, @@ -566,7 +584,7 @@ fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Ex COMPARISON_TO_EMPTY, span, "comparison to empty slice", - &format!("using `{op}is_empty` is clearer and more explicit"), + format!("using `{op}is_empty` is clearer and more explicit"), format!("{op}{lit_str}.is_empty()"), applicability, ); @@ -594,7 +612,7 @@ fn is_empty_array(expr: &Expr<'_>) -> bool { fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// Gets an `AssocItem` and return true if it matches `is_empty(self)`. fn is_is_empty(cx: &LateContext<'_>, item: &ty::AssocItem) -> bool { - if item.kind == ty::AssocKind::Fn { + if item.kind == AssocKind::Fn { let sig = cx.tcx.fn_sig(item.def_id).skip_binder(); let ty = sig.skip_binder(); ty.inputs().len() == 1 diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 57fac35104272..b92364a9d147c 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -16,11 +16,14 @@ rustc::diagnostic_outside_of_impl, rustc::untranslatable_diagnostic )] -#![warn(trivial_casts, trivial_numeric_casts)] -// warn on lints, that are included in `rust-lang/rust`s bootstrap -#![warn(rust_2018_idioms, unused_lifetimes)] -// warn on rustc internal lints -#![warn(rustc::internal)] +#![warn( + trivial_casts, + trivial_numeric_casts, + rust_2018_idioms, + unused_lifetimes, + unused_qualifications, + rustc::internal +)] // Disable this rustc lint for now, as it was also done in rustc #![allow(rustc::potential_query_instability)] @@ -186,6 +189,7 @@ mod large_futures; mod large_include_file; mod large_stack_arrays; mod large_stack_frames; +mod legacy_numeric_constants; mod len_zero; mod let_if_seq; mod let_underscore; @@ -1080,6 +1084,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { allow_one_hash_in_raw_strings, }) }); + store.register_late_pass(move |_| Box::new(legacy_numeric_constants::LegacyNumericConstants::new(msrv()))); store.register_late_pass(|_| Box::new(manual_range_patterns::ManualRangePatterns)); store.register_early_pass(|| Box::new(visibility::Visibility)); store.register_late_pass(move |_| Box::new(tuple_array_conversions::TupleArrayConversions { msrv: msrv() })); diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 2b73663d229ea..a60a40a2a4713 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -216,7 +216,7 @@ fn check_fn_inner<'tcx>( None })) .collect_vec(), - &format!("the following explicit lifetimes could be elided: {lts}"), + format!("the following explicit lifetimes could be elided: {lts}"), |diag| { if sig.header.is_async() { // async functions have usages whose spans point at the lifetime declaration which messes up diff --git a/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs b/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs index 29957e423b0b9..3d1c666dfea06 100644 --- a/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs +++ b/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs @@ -70,7 +70,7 @@ impl LateLintPass<'_> for LinesFilterMapOk { cx, LINES_FILTER_MAP_OK, fm_span, - &format!("`{fm_method_str}()` will run forever if the iterator repeatedly produces an `Err`",), + format!("`{fm_method_str}()` will run forever if the iterator repeatedly produces an `Err`",), |diag| { diag.span_note( fm_receiver.span, diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs index f33151cf4c591..2348dd18220fe 100644 --- a/src/tools/clippy/clippy_lints/src/literal_representation.rs +++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs @@ -158,7 +158,7 @@ enum WarningType { } impl WarningType { - fn display(&self, suggested_format: String, cx: &EarlyContext<'_>, span: rustc_span::Span) { + fn display(&self, suggested_format: String, cx: &EarlyContext<'_>, span: Span) { match self { Self::MistypedLiteralSuffix => span_lint_and_sugg( cx, @@ -302,11 +302,7 @@ impl LiteralDigitGrouping { } // Returns `false` if the check fails - fn check_for_mistyped_suffix( - cx: &EarlyContext<'_>, - span: rustc_span::Span, - num_lit: &mut NumericLiteral<'_>, - ) -> bool { + fn check_for_mistyped_suffix(cx: &EarlyContext<'_>, span: Span, num_lit: &mut NumericLiteral<'_>) -> bool { if num_lit.suffix.is_some() { return true; } diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs index 277062a84901c..f0ee64d714e05 100644 --- a/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs @@ -42,7 +42,7 @@ pub(super) fn check<'tcx>( cx, EXPLICIT_COUNTER_LOOP, span, - &format!("the variable `{name}` is used as a loop counter"), + format!("the variable `{name}` is used as a loop counter"), "consider using", format!( "for ({name}, {}) in {}.enumerate()", @@ -62,7 +62,7 @@ pub(super) fn check<'tcx>( cx, EXPLICIT_COUNTER_LOOP, span, - &format!("the variable `{name}` is used as a loop counter"), + format!("the variable `{name}` is used as a loop counter"), |diag| { diag.span_suggestion( span, diff --git a/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs b/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs index 94c951fc10a6b..6922533fbe9d3 100644 --- a/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs +++ b/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs @@ -37,7 +37,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx cx, FOR_KV_MAP, arg_span, - &format!("you seem to want to iterate on a map's {kind}s"), + format!("you seem to want to iterate on a map's {kind}s"), |diag| { let map = sugg::Sugg::hir(cx, arg, "map"); multispan_sugg( diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs index 36de9021f4996..dbc094a6d73fc 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs @@ -62,7 +62,7 @@ pub(super) fn check<'tcx>( "...and remove the `if let` statement in the for loop" }; - span_lint_and_then(cx, MANUAL_FLATTEN, span, &msg, |diag| { + span_lint_and_then(cx, MANUAL_FLATTEN, span, msg, |diag| { diag.span_suggestion(arg.span, "try", sugg, applicability); diag.span_help(inner_expr.span, help_msg); }); diff --git a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs index c4e60e98ad466..94330001e4f16 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs @@ -109,7 +109,7 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { } } - fn fake_read(&mut self, _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} + fn fake_read(&mut self, _: &PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } impl MutatePairDelegate<'_, '_> { @@ -141,7 +141,7 @@ impl BreakAfterExprVisitor { } } -impl<'tcx> intravisit::Visitor<'tcx> for BreakAfterExprVisitor { +impl<'tcx> Visitor<'tcx> for BreakAfterExprVisitor { fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { if self.past_candidate { return; diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs index cf34c904dfe94..de7ec81bc0108 100644 --- a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs @@ -143,7 +143,7 @@ pub(super) fn check<'tcx>( cx, NEEDLESS_RANGE_LOOP, arg.span, - &format!("the loop variable `{}` is used to index `{indexed}`", ident.name), + format!("the loop variable `{}` is used to index `{indexed}`", ident.name), |diag| { multispan_sugg( diag, @@ -169,7 +169,7 @@ pub(super) fn check<'tcx>( cx, NEEDLESS_RANGE_LOOP, arg.span, - &format!("the loop variable `{}` is only used to index `{indexed}`", ident.name), + format!("the loop variable `{}` is only used to index `{indexed}`", ident.name), |diag| { multispan_sugg( diag, @@ -357,7 +357,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { let def_id = self.cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); for (ty, expr) in iter::zip( self.cx.tcx.fn_sig(def_id).instantiate_identity().inputs().skip_binder(), - std::iter::once(receiver).chain(args.iter()), + iter::once(receiver).chain(args.iter()), ) { self.prefer_mutable = false; if let ty::Ref(_, _, mutbl) = *ty.kind() { diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs index 8aae7be459361..313a5bfefbc8d 100644 --- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs @@ -159,12 +159,9 @@ fn never_loop_expr<'tcx>( | ExprKind::DropTemps(e) => never_loop_expr(cx, e, local_labels, main_loop_id), ExprKind::Let(let_expr) => never_loop_expr(cx, let_expr.init, local_labels, main_loop_id), ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(cx, es.iter(), local_labels, main_loop_id), - ExprKind::MethodCall(_, receiver, es, _) => never_loop_expr_all( - cx, - std::iter::once(receiver).chain(es.iter()), - local_labels, - main_loop_id, - ), + ExprKind::MethodCall(_, receiver, es, _) => { + never_loop_expr_all(cx, once(receiver).chain(es.iter()), local_labels, main_loop_id) + }, ExprKind::Struct(_, fields, base) => { let fields = never_loop_expr_all(cx, fields.iter().map(|f| f.expr), local_labels, main_loop_id); if let Some(base) = base { diff --git a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs index 670a78d58c3c6..1d90d4a58f5e0 100644 --- a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs +++ b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs @@ -31,7 +31,7 @@ pub(super) fn check<'tcx>( vec.span, "it looks like the same item is being pushed into this Vec", None, - &format!("consider using vec![{item_str};SIZE] or {vec_str}.resize(NEW_SIZE, {item_str})"), + format!("consider using vec![{item_str};SIZE] or {vec_str}.resize(NEW_SIZE, {item_str})"), ); } diff --git a/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs b/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs index 4773a1454b7a0..108fdb697752b 100644 --- a/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs @@ -42,7 +42,7 @@ pub(super) fn check<'tcx>( }, [], _, - ) if method.ident.name == rustc_span::sym::iter => (arg, "&"), + ) if method.ident.name == sym::iter => (arg, "&"), ExprKind::MethodCall( method, Expr { @@ -60,7 +60,7 @@ pub(super) fn check<'tcx>( }, [], _, - ) if method.ident.name == rustc_span::sym::into_iter => (arg, ""), + ) if method.ident.name == sym::into_iter => (arg, ""), // Only check for arrays edition 2021 or later, as this case will trigger a compiler error otherwise. ExprKind::Array([arg]) if cx.tcx.sess.edition() >= Edition::Edition2021 => (arg, ""), _ => return, @@ -95,7 +95,7 @@ pub(super) fn check<'tcx>( cx, SINGLE_ELEMENT_LOOP, arg.span, - format!("this loops only once with `{pat_snip}` being `{range_expr}`").as_str(), + format!("this loops only once with `{pat_snip}` being `{range_expr}`"), "did you mean to iterate over the range instead?", sugg.to_string(), Applicability::Unspecified, diff --git a/src/tools/clippy/clippy_lints/src/main_recursion.rs b/src/tools/clippy/clippy_lints/src/main_recursion.rs index a381b35cf2e27..72807b4b284ea 100644 --- a/src/tools/clippy/clippy_lints/src/main_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/main_recursion.rs @@ -51,7 +51,7 @@ impl LateLintPass<'_> for MainRecursion { cx, MAIN_RECURSION, func.span, - &format!("recursing into entrypoint `{}`", snippet(cx, func.span, "main")), + format!("recursing into entrypoint `{}`", snippet(cx, func.span, "main")), None, "consider using another function for this recursion", ); diff --git a/src/tools/clippy/clippy_lints/src/manual_assert.rs b/src/tools/clippy/clippy_lints/src/manual_assert.rs index 4f6a2cf017ca0..76edbe8b755bd 100644 --- a/src/tools/clippy/clippy_lints/src/manual_assert.rs +++ b/src/tools/clippy/clippy_lints/src/manual_assert.rs @@ -1,7 +1,7 @@ use crate::rustc_lint::LintContext; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::root_macro_call; -use clippy_utils::{is_else_clause, peel_blocks_with_stmt, span_extract_comment, sugg}; +use clippy_utils::{is_else_clause, is_parent_stmt, peel_blocks_with_stmt, span_extract_comment, sugg}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; @@ -63,7 +63,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert { _ => (cond, "!"), }; let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par(); - let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip});"); + let semicolon = if is_parent_stmt(cx, expr.hir_id) { ";" } else { "" }; + let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip}){semicolon}"); // we show to the user the suggestion without the comments, but when applying the fix, include the // comments in the block span_lint_and_then( diff --git a/src/tools/clippy/clippy_lints/src/manual_bits.rs b/src/tools/clippy/clippy_lints/src/manual_bits.rs index aa02e4e7a434f..24fc2b4faeacc 100644 --- a/src/tools/clippy/clippy_lints/src/manual_bits.rs +++ b/src/tools/clippy/clippy_lints/src/manual_bits.rs @@ -139,7 +139,7 @@ fn is_ty_conversion(expr: &Expr<'_>) -> bool { if let ExprKind::Cast(..) = expr.kind { true } else if let ExprKind::MethodCall(path, _, [], _) = expr.kind - && path.ident.name == rustc_span::sym::try_into + && path.ident.name == sym::try_into { // This is only called for `usize` which implements `TryInto`. Therefore, // we don't have to check here if `self` implements the `TryInto` trait. diff --git a/src/tools/clippy/clippy_lints/src/manual_clamp.rs b/src/tools/clippy/clippy_lints/src/manual_clamp.rs index 830af77968c0e..1eadc200bedc8 100644 --- a/src/tools/clippy/clippy_lints/src/manual_clamp.rs +++ b/src/tools/clippy/clippy_lints/src/manual_clamp.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::higher::If; use clippy_utils::sugg::Sugg; @@ -17,6 +18,7 @@ use rustc_middle::ty::Ty; use rustc_session::impl_lint_pass; use rustc_span::symbol::sym; use rustc_span::Span; +use std::cmp::Ordering; use std::ops::Deref; declare_clippy_lint! { @@ -26,6 +28,11 @@ declare_clippy_lint! { /// ### Why is this bad? /// clamp is much shorter, easier to read, and doesn't use any control flow. /// + /// ### Limitations + /// + /// This lint will only trigger if max and min are known at compile time, and max is + /// greater than min. + /// /// ### Known issue(s) /// If the clamped variable is NaN this suggestion will cause the code to propagate NaN /// rather than returning either `max` or `min`. @@ -80,7 +87,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.66.0"] pub MANUAL_CLAMP, - nursery, + complexity, "using a clamp pattern instead of the clamp function" } impl_lint_pass!(ManualClamp => [MANUAL_CLAMP]); @@ -103,6 +110,26 @@ struct ClampSuggestion<'tcx> { hir_with_ignore_attr: Option, } +impl<'tcx> ClampSuggestion<'tcx> { + /// This function will return true if and only if you can demonstrate at compile time that min + /// is less than max. + fn min_less_than_max(&self, cx: &LateContext<'tcx>) -> bool { + let max_type = cx.typeck_results().expr_ty(self.params.max); + let min_type = cx.typeck_results().expr_ty(self.params.min); + if max_type != min_type { + return false; + } + if let Some(max) = constant(cx, cx.typeck_results(), self.params.max) + && let Some(min) = constant(cx, cx.typeck_results(), self.params.min) + && let Some(ord) = Constant::partial_cmp(cx.tcx, max_type, &min, &max) + { + ord != Ordering::Greater + } else { + false + } + } +} + #[derive(Debug)] struct InputMinMax<'tcx> { input: &'tcx Expr<'tcx>, @@ -123,7 +150,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualClamp { .or_else(|| is_match_pattern(cx, expr)) .or_else(|| is_if_elseif_pattern(cx, expr)); if let Some(suggestion) = suggestion { - emit_suggestion(cx, &suggestion); + maybe_emit_suggestion(cx, &suggestion); } } } @@ -133,13 +160,16 @@ impl<'tcx> LateLintPass<'tcx> for ManualClamp { return; } for suggestion in is_two_if_pattern(cx, block) { - emit_suggestion(cx, &suggestion); + maybe_emit_suggestion(cx, &suggestion); } } extract_msrv_attr!(LateContext); } -fn emit_suggestion<'tcx>(cx: &LateContext<'tcx>, suggestion: &ClampSuggestion<'tcx>) { +fn maybe_emit_suggestion<'tcx>(cx: &LateContext<'tcx>, suggestion: &ClampSuggestion<'tcx>) { + if !suggestion.min_less_than_max(cx) { + return; + } let ClampSuggestion { params: InputMinMax { input, diff --git a/src/tools/clippy/clippy_lints/src/manual_strip.rs b/src/tools/clippy/clippy_lints/src/manual_strip.rs index bcd0243600245..45af9f07718d2 100644 --- a/src/tools/clippy/clippy_lints/src/manual_strip.rs +++ b/src/tools/clippy/clippy_lints/src/manual_strip.rs @@ -106,12 +106,12 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { cx, MANUAL_STRIP, strippings[0], - &format!("stripping a {kind_word} manually"), + format!("stripping a {kind_word} manually"), |diag| { diag.span_note(test_span, format!("the {kind_word} was tested here")); multispan_sugg( diag, - &format!("try using the `strip_{kind_word}` method"), + format!("try using the `strip_{kind_word}` method"), vec![( test_span, format!( diff --git a/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs b/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs index ddaf97c463ae7..c562ceb5bcee9 100644 --- a/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs +++ b/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs @@ -1,14 +1,15 @@ +use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{Arm, Expr, ExprKind, HirId, LangItem, MatchSource, Pat, PatKind, QPath}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; use rustc_span::sym; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_default_equivalent; use clippy_utils::source::snippet_opt; use clippy_utils::ty::implements_trait; +use clippy_utils::{in_constant, is_default_equivalent, peel_blocks, span_contains_comment}; declare_clippy_lint! { /// ### What it does @@ -118,22 +119,27 @@ fn handle_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { // We now get the bodies for both the `Some` and `None` arms. && let Some(((body_some, binding_id), body_none)) = get_some_and_none_bodies(cx, arm1, arm2) // We check that the `Some(x) => x` doesn't do anything apart "returning" the value in `Some`. - && let ExprKind::Path(QPath::Resolved(_, path)) = body_some.peel_blocks().kind + && let ExprKind::Path(QPath::Resolved(_, path)) = peel_blocks(body_some).kind && let Res::Local(local_id) = path.res && local_id == binding_id // We now check the `None` arm is calling a method equivalent to `Default::default`. - && let body_none = body_none.peel_blocks() + && let body_none = peel_blocks(body_none) && is_default_equivalent(cx, body_none) - && let Some(match_expr_snippet) = snippet_opt(cx, match_expr.span) + && let Some(receiver) = Sugg::hir_opt(cx, match_expr).map(Sugg::maybe_par) { + let applicability = if span_contains_comment(cx.sess().source_map(), expr.span) { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }; span_lint_and_sugg( cx, MANUAL_UNWRAP_OR_DEFAULT, expr.span, "match can be simplified with `.unwrap_or_default()`", "replace it with", - format!("{match_expr_snippet}.unwrap_or_default()"), - Applicability::MachineApplicable, + format!("{receiver}.unwrap_or_default()"), + applicability, ); } true @@ -149,14 +155,19 @@ fn handle_if_let<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { && implements_trait(cx, match_ty, default_trait_id, &[]) && let Some(binding_id) = get_some(cx, let_.pat) // We check that the `Some(x) => x` doesn't do anything apart "returning" the value in `Some`. - && let ExprKind::Path(QPath::Resolved(_, path)) = if_block.peel_blocks().kind + && let ExprKind::Path(QPath::Resolved(_, path)) = peel_blocks(if_block).kind && let Res::Local(local_id) = path.res && local_id == binding_id // We now check the `None` arm is calling a method equivalent to `Default::default`. - && let body_else = else_expr.peel_blocks() + && let body_else = peel_blocks(else_expr) && is_default_equivalent(cx, body_else) && let Some(if_let_expr_snippet) = snippet_opt(cx, let_.init.span) { + let applicability = if span_contains_comment(cx.sess().source_map(), expr.span) { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }; span_lint_and_sugg( cx, MANUAL_UNWRAP_OR_DEFAULT, @@ -164,14 +175,14 @@ fn handle_if_let<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { "if let can be simplified with `.unwrap_or_default()`", "replace it with", format!("{if_let_expr_snippet}.unwrap_or_default()"), - Applicability::MachineApplicable, + applicability, ); } } impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOrDefault { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if expr.span.from_expansion() { + if expr.span.from_expansion() || in_constant(cx, expr.hir_id) { return; } if !handle_match(cx, expr) { diff --git a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs index c9eab7109ebcd..9db04b615be7b 100644 --- a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs +++ b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs @@ -221,13 +221,13 @@ fn lint_map_unit_fn( binding = let_binding_name(cx, var_arg) ); - span_lint_and_then(cx, lint, expr.span, &msg, |diag| { + span_lint_and_then(cx, lint, expr.span, msg, |diag| { diag.span_suggestion(stmt.span, "try", suggestion, applicability); }); } else if let Some((binding, closure_expr)) = unit_closure(cx, fn_arg) { let msg = suggestion_msg("closure", map_type); - span_lint_and_then(cx, lint, expr.span, &msg, |diag| { + span_lint_and_then(cx, lint, expr.span, msg, |diag| { if let Some(reduced_expr_span) = reduce_unit_expression(cx, closure_expr) { let mut applicability = Applicability::MachineApplicable; let suggestion = format!( diff --git a/src/tools/clippy/clippy_lints/src/match_result_ok.rs b/src/tools/clippy/clippy_lints/src/match_result_ok.rs index 62cedc8847b24..2a5fc8b660916 100644 --- a/src/tools/clippy/clippy_lints/src/match_result_ok.rs +++ b/src/tools/clippy/clippy_lints/src/match_result_ok.rs @@ -76,7 +76,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk { MATCH_RESULT_OK, expr.span.with_hi(let_expr.span.hi()), "matching on `Some` with `ok()` is redundant", - &format!("consider matching on `Ok({some_expr_string})` and removing the call to `ok` instead"), + format!("consider matching on `Ok({some_expr_string})` and removing the call to `ok` instead"), sugg, applicability, ); diff --git a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs index 5fef5930fab25..6746920edc516 100644 --- a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs @@ -97,7 +97,7 @@ fn check_arm<'tcx>( } else { String::new() }; - span_lint_and_then(cx, COLLAPSIBLE_MATCH, inner_expr.span, &msg, |diag| { + span_lint_and_then(cx, COLLAPSIBLE_MATCH, inner_expr.span, msg, |diag| { let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]); help_span.push_span_label(binding_span, "replace this binding"); help_span.push_span_label(inner_then_pat.span, format!("with this pattern{replace_msg}")); diff --git a/src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs b/src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs index 0f242e0b9e12b..93d7683d2af81 100644 --- a/src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{path_to_local_id, peel_blocks, strip_pat_refs}; use rustc_errors::Applicability; -use rustc_hir::{ByRef, ExprKind, LetStmt, MatchSource, PatKind, QPath}; +use rustc_hir::{ExprKind, LetStmt, MatchSource, PatKind, QPath}; use rustc_lint::LateContext; use super::INFALLIBLE_DESTRUCTURING_MATCH; @@ -30,7 +30,7 @@ pub(crate) fn check(cx: &LateContext<'_>, local: &LetStmt<'_>) -> bool { format!( "let {}({}{}) = {};", snippet_with_applicability(cx, variant_name.span, "..", &mut applicability), - if binding.0 == ByRef::Yes { "ref " } else { "" }, + binding.prefix_str(), snippet_with_applicability(cx, local.pat.span, "..", &mut applicability), snippet_with_applicability(cx, target.span, "..", &mut applicability), ), diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs index 3e79cabd795f5..9edd6c9540424 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs @@ -34,7 +34,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, scrutinee: cx, MANUAL_UNWRAP_OR, expr.span, - &format!("this pattern reimplements `{ty_name}::unwrap_or`"), + format!("this pattern reimplements `{ty_name}::unwrap_or`"), "replace with", format!("{suggestion}.unwrap_or({reindented_or_body})",), app, diff --git a/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs b/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs index 3f737da92c055..f5da8ec61874e 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs @@ -43,7 +43,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: cx, MATCH_AS_REF, expr.span, - &format!("use `{suggestion}()` instead"), + format!("use `{suggestion}()` instead"), "try", format!( "{}.{suggestion}(){cast}", @@ -67,7 +67,7 @@ fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option { if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind && is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionSome) - && let PatKind::Binding(BindingAnnotation(ByRef::Yes, mutabl), .., ident, _) = first_pat.kind + && let PatKind::Binding(BindingAnnotation(ByRef::Yes(mutabl), _), .., ident, _) = first_pat.kind && let ExprKind::Call(e, [arg]) = peel_blocks(arm.body).kind && is_res_lang_ctor(cx, path_res(cx, e), LangItem::OptionSome) && let ExprKind::Path(QPath::Resolved(_, path2)) = arg.kind diff --git a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs index b062e81cefddd..64cb7a06ce9c1 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs @@ -122,7 +122,7 @@ where cx, MATCH_LIKE_MATCHES_MACRO, expr.span, - &format!( + format!( "{} expression looks like `matches!` macro", if is_if_let { "if let .. else" } else { "match" } ), 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 bd38648bcf1b6..322e9c3ebe5b3 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 @@ -111,7 +111,7 @@ fn lint(cx: &LateContext<'_>, case_method: &CaseMethod, bad_case_span: Span, bad MATCH_STR_CASE_MISMATCH, bad_case_span, "this `match` arm has a differing case than its expression", - &format!("consider changing the case of this arm to respect `{method_str}`"), + format!("consider changing the case of this arm to respect `{method_str}`"), format!("\"{suggestion}\""), Applicability::MachineApplicable, ); diff --git a/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs b/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs index 8a4c0ab906275..d1f637ec78c67 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs @@ -43,7 +43,7 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<' cx, MATCH_WILD_ERR_ARM, arm.pat.span, - &format!("`Err({ident_bind_name})` matches all errors"), + format!("`Err({ident_bind_name})` matches all errors"), None, "match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable", ); diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index 580d4a6429631..fae2c4e4af92e 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -25,14 +25,13 @@ mod try_err; mod wild_in_or_pats; use clippy_config::msrvs::{self, Msrv}; -use clippy_utils::source::{snippet_opt, walk_span_to_context}; -use clippy_utils::{higher, in_constant, is_direct_expn_of, is_span_match, tokenize_with_text}; +use clippy_utils::source::walk_span_to_context; +use clippy_utils::{higher, in_constant, is_direct_expn_of, is_span_match, span_contains_cfg}; use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat}; -use rustc_lexer::TokenKind; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; -use rustc_span::{Span, SpanData, SyntaxContext}; +use rustc_span::{SpanData, SyntaxContext}; declare_clippy_lint! { /// ### What it does @@ -1196,28 +1195,3 @@ fn contains_cfg_arm(cx: &LateContext<'_>, e: &Expr<'_>, scrutinee: &Expr<'_>, ar Err(()) => true, } } - -/// Checks if the given span contains a `#[cfg(..)]` attribute -fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool { - let Some(snip) = snippet_opt(cx, s) else { - // Assume true. This would require either an invalid span, or one which crosses file boundaries. - return true; - }; - let mut iter = tokenize_with_text(&snip); - - // Search for the token sequence [`#`, `[`, `cfg`] - while iter.any(|(t, _)| matches!(t, TokenKind::Pound)) { - let mut iter = iter.by_ref().skip_while(|(t, _)| { - matches!( - t, - TokenKind::Whitespace | TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } - ) - }); - if matches!(iter.next(), Some((TokenKind::OpenBracket, _))) - && matches!(iter.next(), Some((TokenKind::Ident, "cfg"))) - { - return true; - } - } - false -} 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 cee77f62b61e6..fe83e784c3c9c 100644 --- a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs @@ -178,7 +178,7 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool { }, )), ) => { - return !matches!(annot, BindingAnnotation(ByRef::Yes, _)) && pat_ident.name == first_seg.ident.name; + return !matches!(annot, BindingAnnotation(ByRef::Yes(_), _)) && pat_ident.name == first_seg.ident.name; }, // Example: `Custom::TypeA => Custom::TypeB`, or `None => None` (PatKind::Path(QPath::Resolved(_, p_path)), ExprKind::Path(QPath::Resolved(_, e_path))) => { diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs index 6bae51b45b8fa..50cbccc396839 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs @@ -182,7 +182,7 @@ fn get_pat_binding<'tcx>( if let PatKind::Binding(bind_annot, hir_id, ident, _) = pat.kind && hir_id == local { - if matches!(bind_annot.0, rustc_ast::ByRef::Yes) { + if matches!(bind_annot.0, rustc_ast::ByRef::Yes(_)) { let _ = byref_ident.insert(ident); } // the second call of `replace()` returns a `Some(span)`, meaning a multi-binding pattern 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 b5870d94d996a..78973984fb0bb 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 @@ -72,7 +72,7 @@ fn find_match_true<'tcx>( pat: &'tcx Pat<'_>, scrutinee: &'tcx Expr<'_>, span: Span, - message: &str, + message: &'static str, ) { if let PatKind::Lit(lit) = pat.kind && let ExprKind::Lit(lit) = lit.kind @@ -98,7 +98,7 @@ fn find_match_true<'tcx>( span, message, "consider using the condition directly", - sugg.to_string(), + sugg.into_string(), applicability, ); } @@ -227,7 +227,7 @@ fn find_method_sugg_for_if_let<'tcx>( cx, REDUNDANT_PATTERN_MATCHING, let_pat.span, - &format!("redundant pattern matching, consider using `{good_method}`"), + format!("redundant pattern matching, consider using `{good_method}`"), |diag| { // if/while let ... = ... { ... } // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -304,7 +304,7 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op cx, REDUNDANT_PATTERN_MATCHING, span, - &format!("redundant pattern matching, consider using `{good_method}`"), + format!("redundant pattern matching, consider using `{good_method}`"), "try", sugg, Applicability::MachineApplicable, diff --git a/src/tools/clippy/clippy_lints/src/mem_replace.rs b/src/tools/clippy/clippy_lints/src/mem_replace.rs index 1cdb7921f8166..578aa7989e718 100644 --- a/src/tools/clippy/clippy_lints/src/mem_replace.rs +++ b/src/tools/clippy/clippy_lints/src/mem_replace.rs @@ -193,7 +193,7 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr< cx, MEM_REPLACE_WITH_DEFAULT, expr_span, - &format!( + format!( "replacing a value of type `T` with `T::default()` is better expressed using `{top_crate}::mem::take`" ), |diag| { diff --git a/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs b/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs index 08bfa2e009b3f..fb440ce656ed0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs @@ -84,7 +84,7 @@ pub(crate) trait BindInsteadOfMap { "{option_snip}.{}({closure_args_snip} {some_inner_snip})", Self::GOOD_METHOD_NAME ); - span_lint_and_sugg(cx, BIND_INSTEAD_OF_MAP, expr.span, &msg, "try", note, app); + span_lint_and_sugg(cx, BIND_INSTEAD_OF_MAP, expr.span, msg, "try", note, app); true } else { false @@ -114,7 +114,7 @@ pub(crate) trait BindInsteadOfMap { } else { return false; }; - span_lint_and_then(cx, BIND_INSTEAD_OF_MAP, expr.span, &msg, |diag| { + span_lint_and_then(cx, BIND_INSTEAD_OF_MAP, expr.span, msg, |diag| { multispan_sugg_with_applicability( diag, "try", @@ -157,7 +157,7 @@ pub(crate) trait BindInsteadOfMap { cx, BIND_INSTEAD_OF_MAP, expr.span, - &msg, + msg, "use the expression directly", snippet(cx, recv.span, "..").into(), Applicability::MachineApplicable, diff --git a/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs b/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs index baafb7030aa14..a82abc79f2a24 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs @@ -31,7 +31,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E cx, BYTES_NTH, parent.span, - &format!("called `.bytes().nth().unwrap()` on a `{caller_type}`"), + format!("called `.bytes().nth().unwrap()` on a `{caller_type}`"), "try", format!("{receiver}.as_bytes()[{n}]",), applicability, @@ -41,7 +41,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E cx, BYTES_NTH, expr.span, - &format!("called `.bytes().nth()` on a `{caller_type}`"), + format!("called `.bytes().nth()` on a `{caller_type}`"), "try", format!("{receiver}.as_bytes().get({n}).copied()"), applicability, diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs b/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs index c99cec067bf11..4ae0aeea2d1c5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs @@ -30,7 +30,7 @@ pub(super) fn check( cx, lint, info.expr.span, - &format!("you should use the `{suggest}` method"), + format!("you should use the `{suggest}` method"), "like this", format!( "{}{}.{suggest}({})", diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_cmp_with_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/chars_cmp_with_unwrap.rs index d07e45434a7c9..9c45ec2e56cbe 100644 --- a/src/tools/clippy/clippy_lints/src/methods/chars_cmp_with_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/methods/chars_cmp_with_unwrap.rs @@ -23,7 +23,7 @@ pub(super) fn check( cx, lint, info.expr.span, - &format!("you should use the `{suggest}` method"), + format!("you should use the `{suggest}` method"), "like this", format!( "{}{}.{suggest}('{}')", diff --git a/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs b/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs index 67ad58d5a8c64..5389861245a28 100644 --- a/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs +++ b/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_range_full; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use rustc_errors::Applicability; -use rustc_hir as hir; use rustc_hir::{Expr, ExprKind, LangItem, QPath}; use rustc_lint::LateContext; use rustc_span::symbol::sym; @@ -28,7 +27,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span } } -fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, types: &[rustc_span::Symbol]) -> bool { +fn match_acceptable_type(cx: &LateContext<'_>, expr: &Expr<'_>, types: &[rustc_span::Symbol]) -> bool { let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs(); types.iter().any(|&ty| is_type_diagnostic_item(cx, expr_ty, ty)) // String type is a lang item but not a diagnostic item for now so we need a separate check @@ -44,7 +43,7 @@ fn suggest(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span) { cx, CLEAR_WITH_DRAIN, span.with_hi(expr.span.hi()), - &format!("`drain` used to clear a `{ty_name}`"), + format!("`drain` used to clear a `{ty_name}`"), "try", "clear()".to_string(), Applicability::MachineApplicable, diff --git a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs index d4a5de3d1dea8..4e6823e8220ba 100644 --- a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs +++ b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs @@ -69,7 +69,7 @@ pub(super) fn check( _ => false, }, // local binding capturing a reference - Node::LetStmt(l) if matches!(l.pat.kind, PatKind::Binding(BindingAnnotation(ByRef::Yes, _), ..)) => { + Node::LetStmt(l) if matches!(l.pat.kind, PatKind::Binding(BindingAnnotation(ByRef::Yes(_), _), ..)) => { return; }, _ => false, @@ -94,7 +94,7 @@ pub(super) fn check( cx, CLONE_ON_COPY, expr.span, - &with_forced_trimmed_paths!(format!( + with_forced_trimmed_paths!(format!( "using `clone` on type `{ty}` which implements the `Copy` trait" )), help, diff --git a/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs b/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs index 3a8ca37610a95..56171a134522d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs @@ -70,7 +70,7 @@ pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], expr: &Expr<'_>, re cx, DRAIN_COLLECT, expr.span, - &format!("you seem to be trying to move all elements into a new `{typename}`"), + format!("you seem to be trying to move all elements into a new `{typename}`"), "consider using `mem::take`", sugg, Applicability::MachineApplicable, diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs index 4d8fb217f7f53..fba76852344f5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs @@ -142,7 +142,7 @@ pub(super) fn check<'tcx>( cx, EXPECT_FUN_CALL, span_replace_word, - &format!("use of `{name}` followed by a function call"), + format!("use of `{name}` followed by a function call"), "try", format!("unwrap_or_else({closure_args} panic!({sugg}))"), applicability, @@ -160,7 +160,7 @@ pub(super) fn check<'tcx>( cx, EXPECT_FUN_CALL, span_replace_word, - &format!("use of `{name}` followed by a function call"), + format!("use of `{name}` followed by a function call"), "try", format!("unwrap_or_else({closure_args} {{ panic!(\"{{}}\", {arg_root_snippet}) }})"), applicability, diff --git a/src/tools/clippy/clippy_lints/src/methods/filetype_is_file.rs b/src/tools/clippy/clippy_lints/src/methods/filetype_is_file.rs index b05361ab21204..eab536b88a5b3 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filetype_is_file.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filetype_is_file.rs @@ -34,5 +34,5 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr } let lint_msg = format!("`{lint_unary}FileType::is_file()` only {verb} regular files"); let help_msg = format!("use `{help_unary}FileType::is_dir()` instead"); - span_lint_and_help(cx, FILETYPE_IS_FILE, span, &lint_msg, None, &help_msg); + span_lint_and_help(cx, FILETYPE_IS_FILE, span, lint_msg, None, help_msg); } diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs index 9b656531957fc..581e3b308c3cd 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs @@ -16,20 +16,18 @@ use std::borrow::Cow; use super::{MANUAL_FILTER_MAP, MANUAL_FIND_MAP, OPTION_FILTER_MAP, RESULT_FILTER_MAP}; -fn is_method(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol) -> bool { +fn is_method(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol) -> bool { match &expr.kind { - hir::ExprKind::Path(QPath::TypeRelative(_, mname)) => mname.ident.name == method_name, - hir::ExprKind::Path(QPath::Resolved(_, segments)) => { - segments.segments.last().unwrap().ident.name == method_name - }, - hir::ExprKind::MethodCall(segment, _, _, _) => segment.ident.name == method_name, - hir::ExprKind::Closure(&hir::Closure { body, .. }) => { + ExprKind::Path(QPath::TypeRelative(_, mname)) => mname.ident.name == method_name, + ExprKind::Path(QPath::Resolved(_, segments)) => segments.segments.last().unwrap().ident.name == method_name, + ExprKind::MethodCall(segment, _, _, _) => segment.ident.name == method_name, + ExprKind::Closure(&Closure { body, .. }) => { let body = cx.tcx.hir().body(body); let closure_expr = peel_blocks(body.value); match closure_expr.kind { - hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, receiver, ..) => { + ExprKind::MethodCall(PathSegment { ident, .. }, receiver, ..) => { if ident.name == method_name - && let hir::ExprKind::Path(path) = &receiver.kind + && let ExprKind::Path(path) = &receiver.kind && let Res::Local(ref local) = cx.qpath_res(path, receiver.hir_id) && !body.params.is_empty() { @@ -45,10 +43,10 @@ fn is_method(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol) -> } } -fn is_option_filter_map(cx: &LateContext<'_>, filter_arg: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) -> bool { +fn is_option_filter_map(cx: &LateContext<'_>, filter_arg: &Expr<'_>, map_arg: &Expr<'_>) -> bool { is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym!(is_some)) } -fn is_ok_filter_map(cx: &LateContext<'_>, filter_arg: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) -> bool { +fn is_ok_filter_map(cx: &LateContext<'_>, filter_arg: &Expr<'_>, map_arg: &Expr<'_>) -> bool { is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym!(is_ok)) } @@ -267,10 +265,10 @@ impl<'tcx> OffendingFilterExpr<'tcx> { /// is `filter(|x| x.is_some()).map(|x| x.unwrap())` fn is_filter_some_map_unwrap( cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - filter_recv: &hir::Expr<'_>, - filter_arg: &hir::Expr<'_>, - map_arg: &hir::Expr<'_>, + expr: &Expr<'_>, + filter_recv: &Expr<'_>, + filter_arg: &Expr<'_>, + map_arg: &Expr<'_>, ) -> bool { let iterator = is_trait_method(cx, expr, sym::Iterator); let option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(filter_recv), sym::Option); @@ -279,12 +277,7 @@ fn is_filter_some_map_unwrap( } /// is `filter(|x| x.is_ok()).map(|x| x.unwrap())` -fn is_filter_ok_map_unwrap( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - filter_arg: &hir::Expr<'_>, - map_arg: &hir::Expr<'_>, -) -> bool { +fn is_filter_ok_map_unwrap(cx: &LateContext<'_>, expr: &Expr<'_>, filter_arg: &Expr<'_>, map_arg: &Expr<'_>) -> bool { // result has no filter, so we only check for iterators let iterator = is_trait_method(cx, expr, sym::Iterator); iterator && is_ok_filter_map(cx, filter_arg, map_arg) @@ -294,12 +287,12 @@ fn is_filter_ok_map_unwrap( #[allow(clippy::too_many_arguments)] pub(super) fn check( cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - filter_recv: &hir::Expr<'_>, - filter_arg: &hir::Expr<'_>, + expr: &Expr<'_>, + filter_recv: &Expr<'_>, + filter_arg: &Expr<'_>, filter_span: Span, - map_recv: &hir::Expr<'_>, - map_arg: &hir::Expr<'_>, + map_recv: &Expr<'_>, + map_arg: &Expr<'_>, map_span: Span, is_find: bool, ) { @@ -393,7 +386,7 @@ pub(super) fn check( ) }, }; - span_lint_and_then(cx, lint, span, &msg, |diag| { + span_lint_and_then(cx, lint, span, msg, |diag| { diag.span_suggestion(span, "try", sugg, applicability); if let Some((note, span)) = note_and_span { @@ -405,9 +398,9 @@ pub(super) fn check( fn is_find_or_filter<'a>( cx: &LateContext<'a>, - map_recv: &hir::Expr<'_>, - filter_arg: &hir::Expr<'_>, - map_arg: &hir::Expr<'_>, + map_recv: &Expr<'_>, + filter_arg: &Expr<'_>, + map_arg: &Expr<'_>, ) -> Option<(Ident, CheckResult<'a>)> { if is_trait_method(cx, map_recv, sym::Iterator) // filter(|x| ...is_some())... diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs index 8291c373f3711..999df875c753f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_expr_untyped_identity_function, is_trait_method}; +use clippy_utils::{is_expr_identity_function, is_expr_untyped_identity_function, is_trait_method}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -7,8 +7,20 @@ use rustc_span::{sym, Span}; use super::FILTER_MAP_IDENTITY; +fn is_identity(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { + if is_expr_untyped_identity_function(cx, expr) { + return Some(Applicability::MachineApplicable); + } + if is_expr_identity_function(cx, expr) { + return Some(Applicability::Unspecified); + } + None +} + pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_map_arg: &hir::Expr<'_>, filter_map_span: Span) { - if is_trait_method(cx, expr, sym::Iterator) && is_expr_untyped_identity_function(cx, filter_map_arg) { + if is_trait_method(cx, expr, sym::Iterator) + && let Some(applicability) = is_identity(cx, filter_map_arg) + { span_lint_and_sugg( cx, FILTER_MAP_IDENTITY, @@ -16,7 +28,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_map_arg: "use of `filter_map` with an identity function", "try", "flatten()".to_string(), - Applicability::MachineApplicable, + applicability, ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/get_first.rs b/src/tools/clippy/clippy_lints/src/methods/get_first.rs index 55fcf37289404..f4465e654c2e1 100644 --- a/src/tools/clippy/clippy_lints/src/methods/get_first.rs +++ b/src/tools/clippy/clippy_lints/src/methods/get_first.rs @@ -32,7 +32,7 @@ pub(super) fn check<'tcx>( cx, GET_FIRST, expr.span, - &format!("accessing first element with `{slice_name}.get(0)`"), + format!("accessing first element with `{slice_name}.get(0)`"), "try", format!("{slice_name}.first()"), app, @@ -44,7 +44,7 @@ pub(super) fn check<'tcx>( cx, GET_FIRST, expr.span, - &format!("accessing first element with `{slice_name}.get(0)`"), + format!("accessing first element with `{slice_name}.get(0)`"), "try", format!("{slice_name}.front()"), app, diff --git a/src/tools/clippy/clippy_lints/src/methods/get_last_with_len.rs b/src/tools/clippy/clippy_lints/src/methods/get_last_with_len.rs index 3bdc154df0495..6203765113428 100644 --- a/src/tools/clippy/clippy_lints/src/methods/get_last_with_len.rs +++ b/src/tools/clippy/clippy_lints/src/methods/get_last_with_len.rs @@ -44,7 +44,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: cx, GET_LAST_WITH_LEN, expr.span, - &format!("accessing last element with `{recv_snippet}.get({recv_snippet}.len() - 1)`"), + format!("accessing last element with `{recv_snippet}.get({recv_snippet}.len() - 1)`"), "try", format!("{recv_snippet}.{method}()"), applicability, diff --git a/src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs index afdcb3b654922..455274a442854 100644 --- a/src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs @@ -70,7 +70,7 @@ pub(super) fn check<'tcx>( cx, GET_UNWRAP, span, - &format!("called `.get{mut_str}().unwrap()` on a {caller_type}. Using `[]` is more clear and more concise"), + format!("called `.get{mut_str}().unwrap()` on a {caller_type}. Using `[]` is more clear and more concise"), "try", format!( "{borrow_str}{}[{get_args_str}]", diff --git a/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs b/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs index 78a553eb8c0d4..c510cd915d0c9 100644 --- a/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs +++ b/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs @@ -27,7 +27,7 @@ pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv cx, IMPLICIT_CLONE, expr.span, - &format!("implicitly cloning a `{ty_name}` by calling `{method_name}` on its dereferenced type"), + format!("implicitly cloning a `{ty_name}` by calling `{method_name}` on its dereferenced type"), "consider using", if ref_count > 1 { format!("({}{recv_snip}).clone()", "*".repeat(ref_count - 1)) diff --git a/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs b/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs index efc3ddd20b4bd..230a8eb2ec47a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs +++ b/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs @@ -32,7 +32,7 @@ pub fn check( cx, INEFFICIENT_TO_STRING, expr.span, - &format!("calling `to_string` on `{arg_ty}`"), + format!("calling `to_string` on `{arg_ty}`"), |diag| { diag.help(format!( "`{self_ty}` implements `ToString` through a slower blanket impl, but `{deref_self_ty}` has a fast specialization of `ToString`" diff --git a/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs b/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs index 80160d17c82d0..bbc7ce8d78ab9 100644 --- a/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs @@ -27,7 +27,7 @@ pub(super) fn check( cx, INTO_ITER_ON_REF, method_span, - &format!("this `.into_iter()` call is equivalent to `.{method_name}()` and will not consume the `{kind}`",), + format!("this `.into_iter()` call is equivalent to `.{method_name}()` and will not consume the `{kind}`",), "call directly", method_name.to_string(), Applicability::MachineApplicable, diff --git a/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs b/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs index e963950960ae5..210e4ae0a7bc0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs +++ b/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs @@ -36,7 +36,7 @@ pub(super) fn check<'tcx>( cx, IS_DIGIT_ASCII_RADIX, expr.span, - &format!("use of `char::is_digit` with literal radix of {num}"), + format!("use of `char::is_digit` with literal radix of {num}"), "try", format!( "{}.{replacement}()", diff --git a/src/tools/clippy/clippy_lints/src/methods/is_empty.rs b/src/tools/clippy/clippy_lints/src/methods/is_empty.rs index 7fe66062251bb..d921b7ea14f5d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/is_empty.rs +++ b/src/tools/clippy/clippy_lints/src/methods/is_empty.rs @@ -23,7 +23,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, receiver: &Expr<'_ cx, CONST_IS_EMPTY, expr.span, - &format!("this expression always evaluates to {init_is_empty:?}"), + format!("this expression always evaluates to {init_is_empty:?}"), ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs b/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs index dd741cd43f94d..49de83885a1ca 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs @@ -17,7 +17,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, method_name: &str, expr: &hir: cx, ITER_CLONED_COLLECT, to_replace, - &format!( + format!( "called `iter().{method_name}().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \ more readable" ), diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_count.rs b/src/tools/clippy/clippy_lints/src/methods/iter_count.rs index bcddc7c786a50..209cf2fcc0a44 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_count.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_count.rs @@ -37,7 +37,7 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E cx, ITER_COUNT, expr.span, - &format!("called `.{iter_method}().count()` on a `{caller_type}`"), + format!("called `.{iter_method}().count()` on a `{caller_type}`"), "try", format!( "{}.len()", diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs index 9f84321ced4ae..12647ea1ffcb3 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs @@ -55,7 +55,7 @@ fn is_method( } } match expr.kind { - hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, recv, ..) => { + ExprKind::MethodCall(hir::PathSegment { ident, .. }, recv, ..) => { // compare the identifier of the receiver to the parameter // we are in a filter => closure has a single parameter and a single, non-block // expression, this means that the parameter shadows all outside variables with @@ -73,7 +73,7 @@ fn is_method( // This is used to check for complete paths via `|a| std::option::Option::is_some(a)` // this then unwraps to a path with `QPath::TypeRelative` // we pass the params as they've been passed to the current call through the closure - hir::ExprKind::Call(expr, [param]) => { + ExprKind::Call(expr, [param]) => { // this will hit the `QPath::TypeRelative` case and check that the method name is correct if is_method(cx, expr, type_symbol, method_name, params) // we then check that this is indeed passing the parameter of the closure @@ -85,7 +85,7 @@ fn is_method( } false }, - hir::ExprKind::Path(QPath::TypeRelative(ty, mname)) => { + ExprKind::Path(QPath::TypeRelative(ty, mname)) => { let ty = cx.typeck_results().node_type(ty.hir_id); if let Some(did) = cx.tcx.get_diagnostic_item(type_symbol) && ty.ty_adt_def() == cx.tcx.type_of(did).skip_binder().ty_adt_def() @@ -94,7 +94,7 @@ fn is_method( } false }, - hir::ExprKind::Closure(&hir::Closure { body, .. }) => { + ExprKind::Closure(&hir::Closure { body, .. }) => { let body = cx.tcx.hir().body(body); let closure_expr = peel_blocks(body.value); let params = body.params.iter().map(|param| param.pat).collect::>(); @@ -107,8 +107,8 @@ fn is_method( fn parent_is_map(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { if let Some(expr) = get_parent_expr(cx, expr) && is_trait_method(cx, expr, sym::Iterator) - && let hir::ExprKind::MethodCall(path, _, _, _) = expr.kind - && path.ident.name == rustc_span::sym::map + && let ExprKind::MethodCall(path, _, _, _) = expr.kind + && path.ident.name == sym::map { return true; } @@ -148,7 +148,7 @@ fn expression_type( { return None; } - if let hir::ExprKind::MethodCall(_, receiver, _, _) = expr.kind + if let ExprKind::MethodCall(_, receiver, _, _) = expr.kind && let receiver_ty = cx.typeck_results().expr_ty(receiver) && let Some(iter_item_ty) = get_iterator_item_ty(cx, receiver_ty) { diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs index 6394f35f8604f..b9fec0c4f80a6 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs @@ -54,22 +54,21 @@ pub(super) fn check<'tcx>( cx, ITER_KV_MAP, expr.span, - &format!("iterating on a map's {replacement_kind}s"), + format!("iterating on a map's {replacement_kind}s"), "try", format!("{recv_snippet}.{into_prefix}{replacement_kind}s()"), applicability, ); } else { - let ref_annotation = if annotation.0 == ByRef::Yes { "ref " } else { "" }; - let mut_annotation = if annotation.1 == Mutability::Mut { "mut " } else { "" }; span_lint_and_sugg( cx, ITER_KV_MAP, expr.span, - &format!("iterating on a map's {replacement_kind}s"), + format!("iterating on a map's {replacement_kind}s"), "try", format!( - "{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{ref_annotation}{mut_annotation}{bound_ident}| {})", + "{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{}{bound_ident}| {})", + annotation.prefix_str(), snippet_with_applicability(cx, body_expr.span, "/* body */", &mut applicability) ), applicability, diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs b/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs index 5b0b70b4b9659..e31fa2f777d8e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs @@ -28,7 +28,7 @@ pub(super) fn check<'tcx>( cx, ITER_NTH, expr.span, - &format!("called `.{iter_method}().nth()` on a {caller_type}"), + format!("called `.{iter_method}().nth()` on a {caller_type}"), |diag| { let get_method = if iter_method == "iter_mut" { "get_mut" } else { "get" }; diag.span_suggestion_verbose( diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs index 19b7e97339deb..6c9bdcff82622 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs @@ -69,7 +69,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: &str, re cx, ITER_ON_SINGLE_ITEMS, expr.span, - &format!("`{method_name}` call on a collection with only one item"), + format!("`{method_name}` call on a collection with only one item"), "try", sugg, Applicability::MaybeIncorrect, @@ -79,7 +79,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: &str, re cx, ITER_ON_EMPTY_COLLECTIONS, expr.span, - &format!("`{method_name}` call on an empty collection"), + format!("`{method_name}` call on an empty collection"), "try", format!("{top_crate}::iter::empty()"), Applicability::MaybeIncorrect, diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs index b2fe129cd9510..4729481320eb4 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -60,7 +60,7 @@ pub(super) fn check<'tcx>( } if let Op::NeedlessMove(expr) = op { - let rustc_hir::ExprKind::Closure(closure) = expr.kind else { + let ExprKind::Closure(closure) = expr.kind else { return; }; let body @ Body { params: [p], .. } = cx.tcx.hir().body(closure.body) else { diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs b/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs index 2ab721ace8487..1378a07cbc477 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs @@ -20,7 +20,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span cx, ITER_WITH_DRAIN, span.with_hi(expr.span.hi()), - &format!("`drain(..)` used on a `{ty_name}`"), + format!("`drain(..)` used on a `{ty_name}`"), "try", "into_iter()".to_string(), Applicability::MaybeIncorrect, diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs index bf437db7e72ab..9e3b313156eb3 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -50,7 +50,7 @@ pub fn check( super::MANUAL_SATURATING_ARITHMETIC, expr.span, "manual saturating arithmetic", - &format!("consider using `saturating_{arith}`"), + format!("consider using `saturating_{arith}`"), format!( "{}.saturating_{arith}({})", snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability), diff --git a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs index c3c7a3a003307..0901268e9bdc5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs @@ -1,7 +1,7 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; +use clippy_utils::ty::{is_copy, is_type_diagnostic_item, should_call_clone_as_function}; use clippy_utils::{is_diag_trait_item, match_def_path, paths, peel_blocks}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -50,7 +50,7 @@ pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_ let closure_body = cx.tcx.hir().body(body); let closure_expr = peel_blocks(closure_body.value); match closure_body.params[0].pat.kind { - hir::PatKind::Ref(inner, hir::Mutability::Not) => { + hir::PatKind::Ref(inner, Mutability::Not) => { if let hir::PatKind::Binding(hir::BindingAnnotation::NONE, .., name, None) = inner.kind { if ident_eq(name, closure_expr) { lint_explicit_closure(cx, e.span, recv.span, true, msrv); @@ -124,6 +124,7 @@ fn handle_path( && let ty::Ref(_, ty, Mutability::Not) = ty.kind() && let ty::FnDef(_, lst) = cx.typeck_results().expr_ty(arg).kind() && lst.iter().all(|l| l.as_type() == Some(*ty)) + && !should_call_clone_as_function(cx, *ty) { lint_path(cx, e.span, recv.span, is_copy(cx, ty.peel_refs())); } @@ -160,7 +161,7 @@ fn lint_path(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool) { MAP_CLONE, replace, "you are explicitly cloning with `.map()`", - &format!("consider calling the dedicated `{replacement}` method"), + format!("consider calling the dedicated `{replacement}` method"), format!( "{}.{replacement}()", snippet_with_applicability(cx, root, "..", &mut applicability), @@ -183,7 +184,7 @@ fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_cop MAP_CLONE, replace, message, - &format!("consider calling the dedicated `{sugg_method}` method"), + format!("consider calling the dedicated `{sugg_method}` method"), format!( "{}.{sugg_method}()", snippet_with_applicability(cx, root, "..", &mut applicability), 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 26ef0d10fed49..def8be2ef73dc 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs @@ -21,8 +21,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, map_ cx, MAP_FLATTEN, expr.span.with_lo(map_span.lo()), - &format!("called `map(..).flatten()` on `{caller_ty_name}`"), - &format!("try replacing `map` with `{method_to_use}` and remove the `.flatten()`"), + 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})"), applicability, ); 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 6da9a87f5eece..5dd7b1b02adef 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs @@ -29,7 +29,7 @@ pub(super) fn check( MAP_IDENTITY, sugg_span, "unnecessary map of the identity function", - &format!("remove the call to `{name}`"), + format!("remove the call to `{name}`"), String::new(), Applicability::MachineApplicable, ); diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index b6c474212cd4b..2fb317c8c68ed 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -231,8 +231,12 @@ declare_clippy_lint! { /// used instead. /// /// ### Why is this bad? - /// When applicable, `filter_map()` is more clear since it shows that - /// `Option` is used to produce 0 or 1 items. + /// `filter_map()` is known to always produce 0 or 1 output items per input item, + /// rather than however many the inner iterator type produces. + /// Therefore, it maintains the upper bound in `Iterator::size_hint()`, + /// and communicates to the reader that the input items are not being expanded into + /// multiple output items without their having to notice that the mapping function + /// returns an `Option`. /// /// ### Example /// ```no_run @@ -2998,13 +3002,22 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Looks for calls to ` as Any>::type_id`. + /// Looks for calls to `.type_id()` on a `Box`. /// /// ### Why is this bad? - /// This most certainly does not do what the user expects and is very easy to miss. - /// Calling `type_id` on a `Box` calls `type_id` on the `Box<..>` itself, - /// so this will return the `TypeId` of the `Box` type (not the type id - /// of the value referenced by the box!). + /// This almost certainly does not do what the user expects and can lead to subtle bugs. + /// Calling `.type_id()` on a `Box` returns a fixed `TypeId` of the `Box` itself, + /// rather than returning the `TypeId` of the underlying type behind the trait object. + /// + /// For `Box` specifically (and trait objects that have `Any` as its supertrait), + /// this lint will provide a suggestion, which is to dereference the receiver explicitly + /// to go from `Box` to `dyn Any`. + /// This makes sure that `.type_id()` resolves to a dynamic call on the trait object + /// and not on the box. + /// + /// If the fixed `TypeId` of the `Box` is the intended behavior, it's better to be explicit about it + /// and write `TypeId::of::>()`: + /// this makes it clear that a fixed `TypeId` is returned and not the `TypeId` of the implementor. /// /// ### Example /// ```rust,ignore @@ -3024,7 +3037,7 @@ declare_clippy_lint! { #[clippy::version = "1.73.0"] pub TYPE_ID_ON_BOX, suspicious, - "calling `.type_id()` on `Box`" + "calling `.type_id()` on a boxed trait object" } declare_clippy_lint! { @@ -4236,8 +4249,8 @@ impl_lint_pass!(Methods => [ /// Extracts a method call name, args, and `Span` of the method name. pub fn method_call<'tcx>( - recv: &'tcx hir::Expr<'tcx>, -) -> Option<(&'tcx str, &'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>], Span, Span)> { + recv: &'tcx Expr<'tcx>, +) -> Option<(&'tcx str, &'tcx Expr<'tcx>, &'tcx [Expr<'tcx>], Span, Span)> { if let ExprKind::MethodCall(path, receiver, args, call_span) = recv.kind { if !args.iter().any(|e| e.span.from_expansion()) && !receiver.span.from_expansion() { let name = path.ident.name.as_str(); @@ -4248,7 +4261,7 @@ pub fn method_call<'tcx>( } impl<'tcx> LateLintPass<'tcx> for Methods { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if expr.span.from_expansion() { return; } @@ -4256,12 +4269,12 @@ impl<'tcx> LateLintPass<'tcx> for Methods { self.check_methods(cx, expr); match expr.kind { - hir::ExprKind::Call(func, args) => { + ExprKind::Call(func, args) => { from_iter_instead_of_collect::check(cx, expr, args, func); unnecessary_fallible_conversions::check_function(cx, expr, func); manual_c_str_literals::check(cx, expr, func, args, &self.msrv); }, - hir::ExprKind::MethodCall(method_call, receiver, args, _) => { + ExprKind::MethodCall(method_call, receiver, args, _) => { let method_span = method_call.ident.span; or_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), receiver, args); expect_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), receiver, args); @@ -4273,7 +4286,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { single_char_pattern::check(cx, expr, method_call.ident.name, receiver, args); unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, &self.msrv); }, - hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => { + ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => { let mut info = BinaryExprInfo { expr, chain: lhs, @@ -4320,12 +4333,12 @@ impl<'tcx> LateLintPass<'tcx> for Methods { cx, SHOULD_IMPLEMENT_TRAIT, impl_item.span, - &format!( + format!( "method `{}` can be confused for the standard trait method `{}::{}`", method_config.method_name, method_config.trait_name, method_config.method_name ), None, - &format!( + format!( "consider implementing the trait `{}` or choosing a less ambiguous method name", method_config.trait_name ), @@ -4995,9 +5008,9 @@ fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, /// Used for `lint_binary_expr_with_method_call`. #[derive(Copy, Clone)] struct BinaryExprInfo<'a> { - expr: &'a hir::Expr<'a>, - chain: &'a hir::Expr<'a>, - other: &'a hir::Expr<'a>, + expr: &'a Expr<'a>, + chain: &'a Expr<'a>, + other: &'a Expr<'a>, eq: bool, } 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 9e2fd92255e1f..662e7746496a4 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs @@ -107,7 +107,7 @@ pub(super) fn check<'tcx>( span.push_span_label(iter_call.span, "the iterator could be used here instead"); span_lint_hir_and_then( cx, - super::NEEDLESS_COLLECT, + NEEDLESS_COLLECT, collect_expr.hir_id, span, NEEDLESS_COLLECT_MSG, diff --git a/src/tools/clippy/clippy_lints/src/methods/open_options.rs b/src/tools/clippy/clippy_lints/src/methods/open_options.rs index 77484ab91a9d7..d425b505a760c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/open_options.rs +++ b/src/tools/clippy/clippy_lints/src/methods/open_options.rs @@ -151,7 +151,7 @@ fn check_open_options(cx: &LateContext<'_>, settings: &[(OpenOption, Argument, S cx, NONSENSICAL_OPEN_OPTIONS, prev_span, - &format!("the method `{}` is called more than once", &option), + format!("the method `{}` is called more than once", &option), ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs index d7fec360fa2ae..ba167f9d9c230 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs @@ -15,7 +15,7 @@ pub(super) fn check(cx: &LateContext<'_>, cloned_recv: &Expr<'_>, cloned_ident_s cx, OPTION_AS_REF_CLONED, as_ref_ident_span.to(cloned_ident_span), - &format!("cloning an `Option<_>` using `.{method}().cloned()`"), + format!("cloning an `Option<_>` using `.{method}().cloned()`"), "this can be written more concisely by cloning the `Option<_>` directly", "clone".into(), Applicability::MachineApplicable, diff --git a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs index 88e2af15658ff..cb57689b0c41c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs @@ -104,8 +104,8 @@ pub(super) fn check( cx, OPTION_AS_REF_DEREF, expr.span, - &msg, - &suggestion, + msg, + suggestion, hint, Applicability::MachineApplicable, ); diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs index ab36f854fcb1d..efec9dd716dc3 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -97,7 +97,7 @@ pub(super) fn check<'tcx>( } else { "map_or(
, )" }; - let msg = &format!("called `map().unwrap_or({arg})` on an `Option` value"); + let msg = format!("called `map().unwrap_or({arg})` on an `Option` value"); span_lint_and_then(cx, MAP_UNWRAP_OR, expr.span, msg, |diag| { let map_arg_span = map_arg.span; diff --git a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs index 0602eeaa70417..583e04fb4b18a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs @@ -97,7 +97,7 @@ pub(super) fn check<'tcx>( cx, UNWRAP_OR_DEFAULT, method_span.with_hi(span.hi()), - &format!("use of `{name}` to construct default value"), + format!("use of `{name}` to construct default value"), "try", format!("{sugg}()"), Applicability::MachineApplicable, @@ -167,7 +167,7 @@ pub(super) fn check<'tcx>( cx, OR_FUN_CALL, span_replace_word, - &format!("use of `{name}` followed by a function call"), + format!("use of `{name}` followed by a function call"), "try", format!("{name}_{suffix}({sugg})"), app, diff --git a/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs b/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs index 1148628b0844b..28ca76832eb3a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs +++ b/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs @@ -24,7 +24,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &' cx, RANGE_ZIP_WITH_LEN, expr.span, - &format!( + format!( "it is more idiomatic to use `{}.iter().enumerate()`", snippet(cx, recv.span, "_") ), diff --git a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs index ef1baa6c98820..ac5cc2f01e53f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs +++ b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs @@ -39,7 +39,7 @@ pub(super) fn check<'tcx>( && let closure_body = cx.tcx.hir().body(body) && let Some(closure_arg) = closure_body.params.first() { - if let hir::PatKind::Ref(..) = closure_arg.pat.kind { + if let PatKind::Ref(..) = closure_arg.pat.kind { Some(search_snippet.replacen('&', "", 1)) } else if let PatKind::Binding(..) = strip_pat_refs(closure_arg.pat).kind { // `find()` provides a reference to the item, but `any` does not, @@ -62,7 +62,7 @@ pub(super) fn check<'tcx>( cx, SEARCH_IS_SOME, method_span.with_hi(expr.span.hi()), - &msg, + msg, "consider using", format!( "any({})", @@ -76,7 +76,7 @@ pub(super) fn check<'tcx>( cx, SEARCH_IS_SOME, expr.span, - &msg, + msg, "consider using", format!( "!{iter}.any({})", @@ -94,7 +94,7 @@ pub(super) fn check<'tcx>( "" } ); - span_lint_and_help(cx, SEARCH_IS_SOME, expr.span, &msg, None, &hint); + span_lint_and_help(cx, SEARCH_IS_SOME, expr.span, msg, None, hint); } } // lint if `find()` is called by `String` or `&str` @@ -117,7 +117,7 @@ pub(super) fn check<'tcx>( cx, SEARCH_IS_SOME, method_span.with_hi(expr.span.hi()), - &msg, + msg, "consider using", format!("contains({find_arg})"), applicability, @@ -131,7 +131,7 @@ pub(super) fn check<'tcx>( cx, SEARCH_IS_SOME, expr.span, - &msg, + msg, "consider using", format!("!{string}.contains({find_arg})"), applicability, diff --git a/src/tools/clippy/clippy_lints/src/methods/stable_sort_primitive.rs b/src/tools/clippy/clippy_lints/src/methods/stable_sort_primitive.rs index 0f4c97022dbe5..aef14435d8af3 100644 --- a/src/tools/clippy/clippy_lints/src/methods/stable_sort_primitive.rs +++ b/src/tools/clippy/clippy_lints/src/methods/stable_sort_primitive.rs @@ -17,7 +17,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx cx, STABLE_SORT_PRIMITIVE, e.span, - &format!("used `sort` on primitive type `{slice_type}`"), + format!("used `sort` on primitive type `{slice_type}`"), |diag| { let mut app = Applicability::MachineApplicable; let recv_snip = snippet_with_context(cx, recv.span, e.span.ctxt(), "..", &mut app).0; 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 946cdb49d2744..55ae9746298fa 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs @@ -53,7 +53,7 @@ fn lint_needless(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_ cx, NEEDLESS_SPLITN, expr.span, - &format!("unnecessary use of `{r}splitn`"), + format!("unnecessary use of `{r}splitn`"), "try", format!( "{}.{r}split({})", @@ -154,7 +154,7 @@ fn check_manual_split_once_indirect( let self_snip = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0; let pat_snip = snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0; - span_lint_and_then(cx, MANUAL_SPLIT_ONCE, local.span, &msg, |diag| { + span_lint_and_then(cx, MANUAL_SPLIT_ONCE, local.span, msg, |diag| { diag.span_label(first.span, "first usage here"); diag.span_label(second.span, "second usage here"); diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs index c45212581eedb..ff5c1d1a40193 100644 --- a/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs @@ -37,6 +37,6 @@ pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, se ) }; - span_lint_and_note(cx, SUSPICIOUS_SPLITN, expr.span, &msg, None, note_msg); + span_lint_and_note(cx, SUSPICIOUS_SPLITN, expr.span, msg, None, note_msg); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs index 60864902a4890..ce7aefed01f49 100644 --- a/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs @@ -23,7 +23,7 @@ pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) - cx, SUSPICIOUS_TO_OWNED, expr.span, - &with_forced_trimmed_paths!(format!( + with_forced_trimmed_paths!(format!( "this `to_owned` call clones the {input_type} itself and does not cause the {input_type} contents to become owned" )), |diag| { diff --git a/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs b/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs index 4917936a93222..6f9b38fcf83cc 100644 --- a/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs +++ b/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs @@ -5,13 +5,33 @@ use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; +use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_middle::ty::{self, ExistentialPredicate, Ty}; use rustc_span::{sym, Span}; -fn is_dyn_any(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { +/// Checks if the given type is `dyn Any`, or a trait object that has `Any` as a supertrait. +/// Only in those cases will its vtable have a `type_id` method that returns the implementor's +/// `TypeId`, and only in those cases can we give a proper suggestion to dereference the box. +/// +/// If this returns false, then `.type_id()` likely (this may have FNs) will not be what the user +/// expects in any case and dereferencing it won't help either. It will likely require some +/// other changes, but it is still worth emitting a lint. +/// See for more details. +fn is_subtrait_of_any(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { if let ty::Dynamic(preds, ..) = ty.kind() { preds.iter().any(|p| match p.skip_binder() { - ExistentialPredicate::Trait(tr) => cx.tcx.is_diagnostic_item(sym::Any, tr.def_id), + ExistentialPredicate::Trait(tr) => { + cx.tcx.is_diagnostic_item(sym::Any, tr.def_id) + || cx + .tcx + .super_predicates_of(tr.def_id) + .predicates + .iter() + .any(|(clause, _)| { + matches!(clause.kind().skip_binder(), ty::ClauseKind::Trait(super_tr) + if cx.tcx.is_diagnostic_item(sym::Any, super_tr.def_id())) + }) + }, _ => false, }) } else { @@ -26,36 +46,42 @@ pub(super) fn check(cx: &LateContext<'_>, receiver: &Expr<'_>, call_span: Span) && let ty::Ref(_, ty, _) = recv_ty.kind() && let ty::Adt(adt, args) = ty.kind() && adt.is_box() - && is_dyn_any(cx, args.type_at(0)) + && let inner_box_ty = args.type_at(0) + && let ty::Dynamic(..) = inner_box_ty.kind() { + let ty_name = with_forced_trimmed_paths!(ty.to_string()); + span_lint_and_then( cx, TYPE_ID_ON_BOX, call_span, - "calling `.type_id()` on a `Box`", + format!("calling `.type_id()` on `{ty_name}`"), |diag| { let derefs = recv_adjusts .iter() .filter(|adj| matches!(adj.kind, Adjust::Deref(None))) .count(); - let mut sugg = "*".repeat(derefs + 1); - sugg += &snippet(cx, receiver.span, ""); - diag.note( - "this returns the type id of the literal type `Box` instead of the \ + "this returns the type id of the literal type `Box<_>` instead of the \ type id of the boxed value, which is most likely not what you want", ) - .note( - "if this is intentional, use `TypeId::of::>()` instead, \ - which makes it more clear", - ) - .span_suggestion( - receiver.span, - "consider dereferencing first", - format!("({sugg})"), - Applicability::MaybeIncorrect, - ); + .note(format!( + "if this is intentional, use `TypeId::of::<{ty_name}>()` instead, \ + which makes it more clear" + )); + + if is_subtrait_of_any(cx, inner_box_ty) { + let mut sugg = "*".repeat(derefs + 1); + sugg += &snippet(cx, receiver.span, ""); + + diag.span_suggestion( + receiver.span, + "consider dereferencing first", + format!("({sugg})"), + Applicability::MaybeIncorrect, + ); + } }, ); } diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs index fabf3fa0c0c8b..daf99d9861424 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -60,7 +60,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a UNNECESSARY_FIND_MAP }, expr.span, - &format!("this `.{name}` can be written more simply using `.{sugg}`"), + format!("this `.{name}` can be written more simply using `.{sugg}`"), ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs index cc8053ef507a5..f6184222d8e96 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs @@ -58,7 +58,7 @@ pub(super) fn check( cx, UNNECESSARY_GET_THEN_CHECK, both_calls_span, - &format!("unnecessary use of `{snippet}`"), + format!("unnecessary use of `{snippet}`"), "replace it with", suggestion, Applicability::MaybeIncorrect, @@ -70,7 +70,7 @@ pub(super) fn check( cx, UNNECESSARY_GET_THEN_CHECK, both_calls_span, - &format!("unnecessary use of `{snippet}`"), + format!("unnecessary use of `{snippet}`"), |diag| { diag.span_suggestion( full_span, diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 36497d59a5a85..520dcb2d52dcb 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -61,7 +61,7 @@ pub fn check_for_loop_iter( cx, UNNECESSARY_TO_OWNED, expr.span, - &format!("unnecessary use of `{method_name}`"), + format!("unnecessary use of `{method_name}`"), |diag| { // If `check_into_iter_call_arg` called `check_for_loop_iter` because a call to // a `to_owned`-like function was removed, then the next suggestion may be diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs index 1b2bfbf4090ec..494d71fc053f6 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs @@ -63,7 +63,7 @@ pub(super) fn check( let help_message = format!("used `{method}()` on `{constructor}` value"); let suggestion_message = format!("remove the `{constructor}` and `{method}()`"); - span_lint_and_then(cx, UNNECESSARY_LITERAL_UNWRAP, expr.span, &help_message, |diag| { + span_lint_and_then(cx, UNNECESSARY_LITERAL_UNWRAP, expr.span, help_message, |diag| { let suggestions = match (constructor, method, ty) { ("None", "unwrap", _) => Some(vec![(expr.span, "panic!()".to_string())]), ("None", "expect", _) => Some(vec![ 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 e58d477642795..23fc323446e0a 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 @@ -18,8 +18,7 @@ use rustc_lint::LateContext; use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; use rustc_middle::ty::{ - self, ClauseKind, GenericArg, GenericArgKind, GenericArgsRef, ParamTy, ProjectionPredicate, - TraitPredicate, Ty, + self, ClauseKind, GenericArg, GenericArgKind, GenericArgsRef, ParamTy, ProjectionPredicate, TraitPredicate, Ty, }; use rustc_span::{sym, Symbol}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; @@ -138,7 +137,7 @@ fn check_addr_of_expr( cx, UNNECESSARY_TO_OWNED, parent.span, - &format!("unnecessary use of `{method_name}`"), + format!("unnecessary use of `{method_name}`"), "use", format!( "{:&>width$}{receiver_snippet}", @@ -163,7 +162,7 @@ fn check_addr_of_expr( cx, UNNECESSARY_TO_OWNED, parent.span, - &format!("unnecessary use of `{method_name}`"), + format!("unnecessary use of `{method_name}`"), "use", receiver_snippet, Applicability::MachineApplicable, @@ -173,7 +172,7 @@ fn check_addr_of_expr( cx, UNNECESSARY_TO_OWNED, expr.span.with_lo(receiver.span.hi()), - &format!("unnecessary use of `{method_name}`"), + format!("unnecessary use of `{method_name}`"), "remove this", String::new(), Applicability::MachineApplicable, @@ -188,7 +187,7 @@ fn check_addr_of_expr( cx, UNNECESSARY_TO_OWNED, parent.span, - &format!("unnecessary use of `{method_name}`"), + format!("unnecessary use of `{method_name}`"), "use", format!("{receiver_snippet}.as_ref()"), Applicability::MachineApplicable, @@ -232,7 +231,7 @@ fn check_into_iter_call_arg( cx, UNNECESSARY_TO_OWNED, parent.span, - &format!("unnecessary use of `{method_name}`"), + format!("unnecessary use of `{method_name}`"), "use", format!("{receiver_snippet}.iter().{cloned_or_copied}()"), Applicability::MaybeIncorrect, @@ -271,7 +270,7 @@ fn check_split_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symb cx, UNNECESSARY_TO_OWNED, parent.span, - &format!("unnecessary use of `{method_name}`"), + format!("unnecessary use of `{method_name}`"), "use", format!("{receiver_snippet}{as_ref}.split({arg_snippet})"), Applicability::MaybeIncorrect, @@ -350,7 +349,7 @@ fn check_other_call_arg<'tcx>( cx, UNNECESSARY_TO_OWNED, maybe_arg.span, - &format!("unnecessary use of `{method_name}`"), + format!("unnecessary use of `{method_name}`"), "use", format!("{:&>n_refs$}{receiver_snippet}", ""), Applicability::MachineApplicable, @@ -642,7 +641,7 @@ fn check_if_applicable_to_argument<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'tcx cx, UNNECESSARY_TO_OWNED, arg.span, - &format!("unnecessary use of `{method_name}`"), + format!("unnecessary use of `{method_name}`"), "replace it with", if original_arg_ty.is_array() { format!("{snippet}.as_slice()") diff --git a/src/tools/clippy/clippy_lints/src/methods/unwrap_expect_used.rs b/src/tools/clippy/clippy_lints/src/methods/unwrap_expect_used.rs index 7bd16b473ce40..516b8984ad79d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unwrap_expect_used.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unwrap_expect_used.rs @@ -69,7 +69,7 @@ pub(super) fn check( cx, variant.lint(), expr.span, - &format!("used `{}()` on {kind} value", variant.method_name(is_err)), + format!("used `{}()` on {kind} value", variant.method_name(is_err)), |diag| { diag.note(format!("if this value is {none_prefix}`{none_value}`, it will panic")); diff --git a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs index b8baad18cc8db..ae2b6e6347eb0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::walk_ptrs_ty_depth; +use clippy_utils::ty::{should_call_clone_as_function, walk_ptrs_ty_depth}; use clippy_utils::{ get_parent_expr, is_diag_trait_item, match_def_path, path_to_local_id, paths, peel_blocks, strip_pat_refs, }; @@ -68,7 +68,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, cx, USELESS_ASREF, expr.span, - &format!("this call to `{call_name}` does nothing"), + format!("this call to `{call_name}` does nothing"), "try", snippet_with_applicability(cx, recvr.span, "..", &mut applicability).to_string(), applicability, @@ -93,6 +93,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, // And that it only has one argument. && let [arg] = args && is_calling_clone(cx, arg) + // And that we are not recommending recv.clone() over Arc::clone() or similar + && !should_call_clone_as_function(cx, rcv_ty) { lint_as_ref_clone(cx, expr.span.with_hi(parent.span.hi()), recvr, call_name); } @@ -157,7 +159,7 @@ fn lint_as_ref_clone(cx: &LateContext<'_>, span: Span, recvr: &hir::Expr<'_>, ca cx, USELESS_ASREF, span, - &format!("this call to `{call_name}.map(...)` does nothing"), + format!("this call to `{call_name}.map(...)` does nothing"), "try", format!( "{}.clone()", diff --git a/src/tools/clippy/clippy_lints/src/methods/utils.rs b/src/tools/clippy/clippy_lints/src/methods/utils.rs index 3a0305b4d553e..ef00c812d510a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/utils.rs +++ b/src/tools/clippy/clippy_lints/src/methods/utils.rs @@ -3,7 +3,6 @@ use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{get_parent_expr, path_to_local_id, usage}; use rustc_ast::ast; use rustc_errors::Applicability; -use rustc_hir as hir; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Pat}; use rustc_lint::LateContext; @@ -13,9 +12,9 @@ use rustc_span::symbol::sym; pub(super) fn derefs_to_slice<'tcx>( cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'tcx>, + expr: &'tcx Expr<'tcx>, ty: Ty<'tcx>, -) -> Option<&'tcx hir::Expr<'tcx>> { +) -> Option<&'tcx Expr<'tcx>> { fn may_slice<'a>(cx: &LateContext<'a>, ty: Ty<'a>) -> bool { match ty.kind() { ty::Slice(_) => true, @@ -27,7 +26,7 @@ pub(super) fn derefs_to_slice<'tcx>( } } - if let hir::ExprKind::MethodCall(path, self_arg, ..) = &expr.kind { + if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind { if path.ident.name == sym::iter && may_slice(cx, cx.typeck_results().expr_ty(self_arg)) { Some(self_arg) } else { @@ -51,10 +50,10 @@ pub(super) fn derefs_to_slice<'tcx>( pub(super) fn get_hint_if_single_char_arg( cx: &LateContext<'_>, - arg: &hir::Expr<'_>, + arg: &Expr<'_>, applicability: &mut Applicability, ) -> Option { - if let hir::ExprKind::Lit(lit) = &arg.kind + if let ExprKind::Lit(lit) = &arg.kind && let ast::LitKind::Str(r, style) = lit.node && let string = r.as_str() && string.chars().count() == 1 diff --git a/src/tools/clippy/clippy_lints/src/methods/verbose_file_reads.rs b/src/tools/clippy/clippy_lints/src/methods/verbose_file_reads.rs index 2fe5ae9a9ad8f..181b413a18288 100644 --- a/src/tools/clippy/clippy_lints/src/methods/verbose_file_reads.rs +++ b/src/tools/clippy/clippy_lints/src/methods/verbose_file_reads.rs @@ -17,7 +17,7 @@ pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, - (msg, help): (&str, &str), + (msg, help): (&'static str, &'static str), ) { if is_trait_method(cx, expr, sym::IoRead) && matches!(recv.kind, ExprKind::Path(QPath::Resolved(None, _))) diff --git a/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs b/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs index 0a810a13f3f9e..28068c6347325 100644 --- a/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs +++ b/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs @@ -137,7 +137,7 @@ pub(super) fn check<'tcx>( cx, WRONG_SELF_CONVENTION, first_arg_span, - &format!( + format!( "{suggestion} usually take {}", &self_kinds .iter() diff --git a/src/tools/clippy/clippy_lints/src/min_ident_chars.rs b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs index 0016fb3351739..c6b7f5b0ce277 100644 --- a/src/tools/clippy/clippy_lints/src/min_ident_chars.rs +++ b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs @@ -181,7 +181,7 @@ fn emit_min_ident_chars(conf: &MinIdentChars, cx: &impl LintContext, ident: &str conf.min_ident_chars_threshold, )) }; - span_lint(cx, MIN_IDENT_CHARS, span, &help); + span_lint(cx, MIN_IDENT_CHARS, span, help); } /// Attempt to convert the node to an [`ItemKind::Use`] node. diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs index ea6e662b4be36..f5ce8dd29b1a0 100644 --- a/src/tools/clippy/clippy_lints/src/misc.rs +++ b/src/tools/clippy/clippy_lints/src/misc.rs @@ -129,7 +129,7 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { if !is_lint_allowed(cx, REF_PATTERNS, arg.pat.hir_id) { return; } - if let PatKind::Binding(BindingAnnotation(ByRef::Yes, _), ..) = arg.pat.kind { + if let PatKind::Binding(BindingAnnotation(ByRef::Yes(_), _), ..) = arg.pat.kind { span_lint( cx, TOPLEVEL_REF_ARG, @@ -144,7 +144,7 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { if !in_external_macro(cx.tcx.sess, stmt.span) && let StmtKind::Let(local) = stmt.kind - && let PatKind::Binding(BindingAnnotation(ByRef::Yes, mutabl), .., name, None) = local.pat.kind + && let PatKind::Binding(BindingAnnotation(ByRef::Yes(mutabl), _), .., name, None) = local.pat.kind && let Some(init) = local.init // Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue. && is_lint_allowed(cx, REF_PATTERNS, local.pat.hir_id) @@ -246,7 +246,7 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { cx, USED_UNDERSCORE_BINDING, expr.span, - &format!( + format!( "used binding `{name}` which is prefixed with an underscore. A leading \ underscore signals that a binding will not be used" ), diff --git a/src/tools/clippy/clippy_lints/src/misc_early/builtin_type_shadow.rs b/src/tools/clippy/clippy_lints/src/misc_early/builtin_type_shadow.rs index 9f6b0bdc7a45a..662f7cd8500cf 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/builtin_type_shadow.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/builtin_type_shadow.rs @@ -12,7 +12,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, param: &GenericParam) { cx, BUILTIN_TYPE_SHADOW, param.ident.span, - &format!("this generic shadows the built-in type `{}`", prim_ty.name()), + format!("this generic shadows the built-in type `{}`", prim_ty.name()), ); } } diff --git a/src/tools/clippy/clippy_lints/src/misc_early/literal_suffix.rs b/src/tools/clippy/clippy_lints/src/misc_early/literal_suffix.rs index eda4376f200ee..e0a5e401a50aa 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/literal_suffix.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/literal_suffix.rs @@ -16,7 +16,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, lit_snip: &str, suffi cx, SEPARATED_LITERAL_SUFFIX, lit_span, - &format!("{sugg_type} type suffix should not be separated by an underscore"), + format!("{sugg_type} type suffix should not be separated by an underscore"), "remove the underscore", format!("{}{suffix}", &lit_snip[..maybe_last_sep_idx]), Applicability::MachineApplicable, @@ -26,7 +26,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, lit_snip: &str, suffi cx, UNSEPARATED_LITERAL_SUFFIX, lit_span, - &format!("{sugg_type} type suffix should be separated by an underscore"), + format!("{sugg_type} type suffix should be separated by an underscore"), "add an underscore", format!("{}_{suffix}", &lit_snip[..=maybe_last_sep_idx]), Applicability::MachineApplicable, diff --git a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs index abe5b00e888a4..2f5499d7656fd 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs @@ -394,7 +394,7 @@ impl EarlyLintPass for MiscEarlyLints { cx, DUPLICATE_UNDERSCORE_ARGUMENT, *correspondence, - &format!( + format!( "`{arg_name}` already exists, having another argument having almost the same \ name makes code comprehension and documentation more difficult" ), diff --git a/src/tools/clippy/clippy_lints/src/misc_early/redundant_pattern.rs b/src/tools/clippy/clippy_lints/src/misc_early/redundant_pattern.rs index d7bb0616acb0b..d5b5b2bf2dd1b 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/redundant_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/redundant_pattern.rs @@ -12,7 +12,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { cx, REDUNDANT_PATTERN, pat.span, - &format!( + format!( "the `{} @ _` pattern can be written as just `{}`", ident.name, ident.name, ), diff --git a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs index 676e5d40bb776..cb305cf5582d1 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs @@ -27,7 +27,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { pat.span, "all the struct fields are matched to a wildcard pattern, consider using `..`", None, - &format!("try with `{type_name} {{ .. }}` instead"), + format!("try with `{type_name} {{ .. }}` instead"), ); return; } @@ -63,7 +63,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { "you matched a field with a wildcard pattern, consider using `..` \ instead", None, - &format!("try with `{type_name} {{ {}, .. }}`", normal[..].join(", ")), + format!("try with `{type_name} {{ {}, .. }}`", normal[..].join(", ")), ); } } diff --git a/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs b/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs index 0739b49fe190e..0842a87282475 100644 --- a/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs +++ b/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs @@ -101,7 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch { "try `{}`, or a name that does not conflict with `{type_name}`'s generic params", type_param_names[i] ); - span_lint_and_help(cx, MISMATCHING_TYPE_PARAM_ORDER, *impl_param_span, &msg, None, &help); + span_lint_and_help(cx, MISMATCHING_TYPE_PARAM_ORDER, *impl_param_span, msg, None, help); } } } diff --git a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs index 39d4ea74b3121..c29e46b941c60 100644 --- a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs @@ -65,7 +65,7 @@ declare_clippy_lint! { } declare_lint_pass!(MissingAssertsForIndexing => [MISSING_ASSERTS_FOR_INDEXING]); -fn report_lint(cx: &LateContext<'_>, full_span: Span, msg: &str, indexes: &[Span], f: F) +fn report_lint(cx: &LateContext<'_>, full_span: Span, msg: &'static str, indexes: &[Span], f: F) where F: FnOnce(&mut Diag<'_, ()>), { diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs index 2773427e72d5a..2fb784dae1cd4 100644 --- a/src/tools/clippy/clippy_lints/src/missing_doc.rs +++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs @@ -127,7 +127,7 @@ impl MissingDoc { cx, MISSING_DOCS_IN_PRIVATE_ITEMS, sp, - &format!("missing documentation for {article} {desc}"), + format!("missing documentation for {article} {desc}"), ); } } diff --git a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs index 3bf9f75e22619..a64faa124f086 100644 --- a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs +++ b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs @@ -198,7 +198,7 @@ fn check_struct<'tcx>( } impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx rustc_hir::Item<'tcx>) { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { // is this an `impl Debug for X` block? if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), self_ty, items, .. }) = item.kind && let Res::Def(DefKind::Trait, trait_def_id) = trait_ref.path.res diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index 7393b39c8f60b..c6a76478806ac 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -64,7 +64,7 @@ fn check_missing_inline_attrs(cx: &LateContext<'_>, attrs: &[ast::Attribute], sp cx, MISSING_INLINE_IN_PUBLIC_ITEMS, sp, - &format!("missing `#[inline]` for {desc}"), + format!("missing `#[inline]` for {desc}"), ); } } diff --git a/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs b/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs index 6bbf18d52d11c..6f844bc646a24 100644 --- a/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs +++ b/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs @@ -89,7 +89,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingTraitMethods { cx, MISSING_TRAIT_METHODS, source_map.guess_head_span(item.span), - &format!("missing trait method provided by default: `{}`", assoc.name), + format!("missing trait method provided by default: `{}`", assoc.name), Some(definition_span), "implement the method", ); diff --git a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs index 656fb907fcda0..181351910db62 100644 --- a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs @@ -325,7 +325,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> { self.cx, MIXED_READ_WRITE_IN_EXPRESSION, expr.span, - &format!("unsequenced read of `{}`", self.cx.tcx.hir().name(self.var)), + format!("unsequenced read of `{}`", self.cx.tcx.hir().name(self.var)), Some(self.write_expr.span), "whether read occurs before this write depends on evaluation order", ); diff --git a/src/tools/clippy/clippy_lints/src/module_style.rs b/src/tools/clippy/clippy_lints/src/module_style.rs index 0226b31dd190d..6c031c081750a 100644 --- a/src/tools/clippy/clippy_lints/src/module_style.rs +++ b/src/tools/clippy/clippy_lints/src/module_style.rs @@ -129,9 +129,9 @@ impl EarlyLintPass for ModStyle { cx, SELF_NAMED_MODULE_FILES, Span::new(file.start_pos, file.start_pos, SyntaxContext::root(), None), - &format!("`mod.rs` files are required, found `{}`", path.display()), + format!("`mod.rs` files are required, found `{}`", path.display()), None, - &format!("move `{}` to `{}`", path.display(), correct.display(),), + format!("move `{}` to `{}`", path.display(), correct.display(),), ); } } @@ -169,9 +169,9 @@ fn check_self_named_mod_exists(cx: &EarlyContext<'_>, path: &Path, file: &Source cx, MOD_MODULE_FILES, Span::new(file.start_pos, file.start_pos, SyntaxContext::root(), None), - &format!("`mod.rs` files are not allowed, found `{}`", path.display()), + format!("`mod.rs` files are not allowed, found `{}`", path.display()), None, - &format!("move `{}` to `{}`", path.display(), mod_file.display()), + format!("move `{}` to `{}`", path.display(), mod_file.display()), ); } } diff --git a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs index 648d780ac0939..0e13806678059 100644 --- a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -77,7 +77,7 @@ impl<'tcx> LateLintPass<'tcx> for MultipleUnsafeOpsPerBlock { cx, MULTIPLE_UNSAFE_OPS_PER_BLOCK, block.span, - &format!( + format!( "this `unsafe` block contains {} unsafe operations, expected only one", unsafe_ops.len() ), diff --git a/src/tools/clippy/clippy_lints/src/mut_reference.rs b/src/tools/clippy/clippy_lints/src/mut_reference.rs index 14a1e6be7388d..6867f76a72350 100644 --- a/src/tools/clippy/clippy_lints/src/mut_reference.rs +++ b/src/tools/clippy/clippy_lints/src/mut_reference.rs @@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed { let method_type = cx.tcx.type_of(def_id).instantiate(cx.tcx, args); check_arguments( cx, - std::iter::once(receiver).chain(arguments.iter()).collect(), + iter::once(receiver).chain(arguments.iter()).collect(), method_type, path.ident.as_str(), "method", @@ -83,14 +83,13 @@ fn check_arguments<'tcx>( let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs(); for (argument, parameter) in iter::zip(arguments, parameters) { match parameter.kind() { - ty::Ref(_, _, Mutability::Not) - | ty::RawPtr(_, Mutability::Not) => { + ty::Ref(_, _, Mutability::Not) | ty::RawPtr(_, Mutability::Not) => { if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _) = argument.kind { span_lint( cx, UNNECESSARY_MUT_PASSED, argument.span, - &format!("the {fn_kind} `{name}` doesn't need a mutable reference"), + format!("the {fn_kind} `{name}` doesn't need a mutable reference"), ); } }, diff --git a/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs b/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs index 96cd81ecdf314..e92ba93942eff 100644 --- a/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs +++ b/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs @@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall { cx, DEBUG_ASSERT_WITH_MUT_CALL, span, - &format!("do not call a function with mutable arguments inside of `{macro_name}!`"), + format!("do not call a function with mutable arguments inside of `{macro_name}!`"), ); } } diff --git a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs index 61243c8373168..7ecc86176942b 100644 --- a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs +++ b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs @@ -92,9 +92,9 @@ impl<'tcx> LateLintPass<'tcx> for Mutex { behavior and not the internal type, consider using `Mutex<()>`" ); match *mutex_param.kind() { - ty::Uint(t) if t != ty::UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, &msg), - ty::Int(t) if t != ty::IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, &msg), - _ => span_lint(cx, MUTEX_ATOMIC, expr.span, &msg), + ty::Uint(t) if t != UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), + ty::Int(t) if t != IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), + _ => span_lint(cx, MUTEX_ATOMIC, expr.span, msg), }; } } diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs index 166a7f71d695b..f9ee4a3dc93a2 100644 --- a/src/tools/clippy/clippy_lints/src/needless_bool.rs +++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs @@ -6,11 +6,12 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::{ - higher, is_else_clause, is_expn_of, peel_blocks, peel_blocks_with_stmt, span_extract_comment, SpanlessEq, + higher, is_else_clause, is_expn_of, is_parent_stmt, peel_blocks, peel_blocks_with_stmt, span_extract_comment, + SpanlessEq, }; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Node, UnOp}; +use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; @@ -135,13 +136,6 @@ fn condition_needs_parentheses(e: &Expr<'_>) -> bool { false } -fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool { - matches!( - cx.tcx.parent_hir_node(id), - Node::Stmt(..) | Node::Block(Block { stmts: &[], .. }) - ) -} - impl<'tcx> LateLintPass<'tcx> for NeedlessBool { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { use self::Expression::{Bool, RetBool}; @@ -323,11 +317,11 @@ fn one_side_is_unary_not<'tcx>(left_side: &'tcx Expr<'_>, right_side: &'tcx Expr fn check_comparison<'a, 'tcx>( cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, - left_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>, - left_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>, - right_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>, - right_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>, - no_literal: Option<(impl FnOnce(Sugg<'a>, Sugg<'a>) -> Sugg<'a>, &str)>, + left_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>, + left_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>, + right_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>, + right_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>, + no_literal: Option<(impl FnOnce(Sugg<'a>, Sugg<'a>) -> Sugg<'a>, &'static str)>, ) { if let ExprKind::Binary(op, left_side, right_side) = e.kind { let (l_ty, r_ty) = ( @@ -397,7 +391,7 @@ fn check_comparison<'a, 'tcx>( binop_span, m, "try simplifying it as shown", - h(left_side, right_side).to_string(), + h(left_side, right_side).into_string(), applicability, ); }), @@ -412,7 +406,7 @@ fn suggest_bool_comparison<'a, 'tcx>( span: Span, expr: &Expr<'_>, mut app: Applicability, - message: &str, + message: &'static str, conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>, ) { let hint = Sugg::hir_with_context(cx, expr, span.ctxt(), "..", &mut app); @@ -422,7 +416,7 @@ fn suggest_bool_comparison<'a, 'tcx>( span, message, "try simplifying it as shown", - conv_hint(hint).to_string(), + conv_hint(hint).into_string(), app, ); } diff --git a/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs b/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs index 4710a69443b47..d91329eadcb43 100644 --- a/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs +++ b/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs @@ -119,7 +119,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef { fn check_subpatterns<'tcx>( cx: &LateContext<'tcx>, - message: &str, + message: &'static str, ref_pat: &Pat<'_>, pat: &Pat<'_>, subpatterns: impl IntoIterator>, 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 a32bca3d03816..c555fc8675c86 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 @@ -381,7 +381,7 @@ fn replace_types<'tcx>( fn_sig: FnSig<'tcx>, arg_index: usize, projection_predicates: &[ProjectionPredicate<'tcx>], - args: &mut [ty::GenericArg<'tcx>], + args: &mut [GenericArg<'tcx>], ) -> bool { let mut replaced = BitSet::new_empty(args.len()); @@ -399,7 +399,7 @@ fn replace_types<'tcx>( return false; } - args[param_ty.index as usize] = ty::GenericArg::from(new_ty); + args[param_ty.index as usize] = GenericArg::from(new_ty); // The `replaced.insert(...)` check provides some protection against infinite loops. if replaced.insert(param_ty.index) { @@ -414,7 +414,7 @@ fn replace_types<'tcx>( )); if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection) - && args[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty) + && args[term_param_ty.index as usize] != GenericArg::from(projected_ty) { deque.push_back((*term_param_ty, projected_ty)); } diff --git a/src/tools/clippy/clippy_lints/src/needless_continue.rs b/src/tools/clippy/clippy_lints/src/needless_continue.rs index ff72b5e69ef1b..8b4a12bb76648 100644 --- a/src/tools/clippy/clippy_lints/src/needless_continue.rs +++ b/src/tools/clippy/clippy_lints/src/needless_continue.rs @@ -313,7 +313,7 @@ fn emit_warning(cx: &EarlyContext<'_>, data: &LintData<'_>, header: &str, typ: L expr.span, message, None, - &format!("{header}\n{snip}"), + format!("{header}\n{snip}"), ); } diff --git a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs index d7adf22ff32a3..37463cfec9a21 100644 --- a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs @@ -131,7 +131,7 @@ fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { NEEDLESS_QUESTION_MARK, expr.span, "question mark operator is useless here", - &format!("try removing question mark and `{sugg_remove}`"), + format!("try removing question mark and `{sugg_remove}`"), format!("{}", snippet(cx, inner_expr.span, r#""...""#)), Applicability::MachineApplicable, ); diff --git a/src/tools/clippy/clippy_lints/src/new_without_default.rs b/src/tools/clippy/clippy_lints/src/new_without_default.rs index 627b4968d9f68..78dd1e051627b 100644 --- a/src/tools/clippy/clippy_lints/src/new_without_default.rs +++ b/src/tools/clippy/clippy_lints/src/new_without_default.rs @@ -134,9 +134,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { NEW_WITHOUT_DEFAULT, id.into(), impl_item.span, - &format!( - "you should consider adding a `Default` implementation for `{self_type_snip}`" - ), + format!("you should consider adding a `Default` implementation for `{self_type_snip}`"), |diag| { diag.suggest_prepend_item( cx, diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index 43810ec0ec743..f915145e794bc 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -107,7 +107,7 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect { } } - fn check_expr(&mut self, _: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { + fn check_expr(&mut self, _: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let Some(def_id) = path_to_local(expr) { // FIXME(rust/#120456) - is `swap_remove` correct? self.underscore_bindings.swap_remove(&def_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 6cb84bb78b6d6..73fc34c2450f9 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -285,7 +285,7 @@ impl NonCopyConst { let def_id = body_id.hir_id.owner.to_def_id(); let args = ty::GenericArgs::identity_for_item(cx.tcx, def_id); let instance = ty::Instance::new(def_id, args); - let cid = rustc_middle::mir::interpret::GlobalId { + let cid = GlobalId { instance, promoted: None, }; @@ -534,7 +534,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { } } -fn ignored_macro(cx: &LateContext<'_>, it: &rustc_hir::Item<'_>) -> bool { +fn ignored_macro(cx: &LateContext<'_>, it: &Item<'_>) -> bool { macro_backtrace(it.span).any(|macro_call| { matches!( cx.tcx.get_diagnostic_name(macro_call.def_id), 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 b8c3c7fa65aee..7b26235291a56 100644 --- a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs +++ b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs @@ -111,7 +111,7 @@ impl<'a, 'tcx> SimilarNamesLocalVisitor<'a, 'tcx> { self.cx, MANY_SINGLE_CHAR_NAMES, span, - &format!("{num_single_char_names} bindings with single-character names in scope"), + format!("{num_single_char_names} bindings with single-character names in scope"), ); } } diff --git a/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs b/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs index 408216229a41c..74e6c57b52d46 100644 --- a/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs +++ b/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs @@ -119,7 +119,7 @@ impl<'tcx> LateLintPass<'tcx> for NonSendFieldInSendTy { cx, NON_SEND_FIELDS_IN_SEND_TY, item.span, - &format!( + format!( "some fields in `{}` are not safe to be sent to another thread", snippet(cx, hir_impl.self_ty.span, "Unknown") ), diff --git a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs index 1c6069e9c6507..88f2eabaccba7 100644 --- a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs +++ b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs @@ -121,7 +121,7 @@ fn emit_help(cx: &EarlyContext<'_>, snip: &str, (open, close): (char, char), spa cx, NONSTANDARD_MACRO_BRACES, span, - &format!("use of irregular braces for `{macro_name}!` macro"), + format!("use of irregular braces for `{macro_name}!` macro"), "consider writing", format!("{macro_name}!{open}{macro_args}{close}"), Applicability::MachineApplicable, diff --git a/src/tools/clippy/clippy_lints/src/octal_escapes.rs b/src/tools/clippy/clippy_lints/src/octal_escapes.rs index 8822dfeedddb7..2fc039ae886e4 100644 --- a/src/tools/clippy/clippy_lints/src/octal_escapes.rs +++ b/src/tools/clippy/clippy_lints/src/octal_escapes.rs @@ -94,7 +94,7 @@ fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) { cx, OCTAL_ESCAPES, span, - &format!( + format!( "octal-looking escape in {} literal", if is_string { "string" } else { "byte string" } ), diff --git a/src/tools/clippy/clippy_lints/src/operators/absurd_extreme_comparisons.rs b/src/tools/clippy/clippy_lints/src/operators/absurd_extreme_comparisons.rs index f4863600ccc04..9769da6d3e9b1 100644 --- a/src/tools/clippy/clippy_lints/src/operators/absurd_extreme_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/operators/absurd_extreme_comparisons.rs @@ -42,7 +42,7 @@ pub(super) fn check<'tcx>( } ); - span_lint_and_help(cx, ABSURD_EXTREME_COMPARISONS, expr.span, msg, None, &help); + span_lint_and_help(cx, ABSURD_EXTREME_COMPARISONS, expr.span, msg, None, help); } } diff --git a/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs b/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs index 2e026c369ee3f..545e680ce0de5 100644 --- a/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs +++ b/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs @@ -64,7 +64,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!("incompatible bit mask: `_ & {mask_value}` can never be equal to `{cmp_value}`"), + format!("incompatible bit mask: `_ & {mask_value}` can never be equal to `{cmp_value}`"), ); } } else if mask_value == 0 { @@ -77,7 +77,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!("incompatible bit mask: `_ | {mask_value}` can never be equal to `{cmp_value}`"), + format!("incompatible bit mask: `_ | {mask_value}` can never be equal to `{cmp_value}`"), ); } }, @@ -90,7 +90,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!("incompatible bit mask: `_ & {mask_value}` will always be lower than `{cmp_value}`"), + format!("incompatible bit mask: `_ & {mask_value}` will always be lower than `{cmp_value}`"), ); } else if mask_value == 0 { span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero"); @@ -102,7 +102,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!("incompatible bit mask: `_ | {mask_value}` will never be lower than `{cmp_value}`"), + format!("incompatible bit mask: `_ | {mask_value}` will never be lower than `{cmp_value}`"), ); } else { check_ineffective_lt(cx, span, mask_value, cmp_value, "|"); @@ -118,7 +118,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!("incompatible bit mask: `_ & {mask_value}` will never be higher than `{cmp_value}`"), + format!("incompatible bit mask: `_ & {mask_value}` will never be higher than `{cmp_value}`"), ); } else if mask_value == 0 { span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero"); @@ -130,7 +130,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!("incompatible bit mask: `_ | {mask_value}` will always be higher than `{cmp_value}`"), + format!("incompatible bit mask: `_ | {mask_value}` will always be higher than `{cmp_value}`"), ); } else { check_ineffective_gt(cx, span, mask_value, cmp_value, "|"); @@ -149,7 +149,7 @@ fn check_ineffective_lt(cx: &LateContext<'_>, span: Span, m: u128, c: u128, op: cx, INEFFECTIVE_BIT_MASK, span, - &format!("ineffective bit mask: `x {op} {m}` compared to `{c}`, is the same as x compared directly"), + format!("ineffective bit mask: `x {op} {m}` compared to `{c}`, is the same as x compared directly"), ); } } @@ -160,7 +160,7 @@ fn check_ineffective_gt(cx: &LateContext<'_>, span: Span, m: u128, c: u128, op: cx, INEFFECTIVE_BIT_MASK, span, - &format!("ineffective bit mask: `x {op} {m}` compared to `{c}`, is the same as x compared directly"), + format!("ineffective bit mask: `x {op} {m}` compared to `{c}`, is the same as x compared directly"), ); } } diff --git a/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs b/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs index e278cf9835a90..7bf9b8ef866a2 100644 --- a/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs @@ -89,7 +89,7 @@ pub(super) fn check<'tcx>( span, "left-hand side of `&&` operator has no effect", Some(left_cond.span.until(right_cond.span)), - &format!("`if `{rhs_str}` evaluates to true, {lhs_str}` will always evaluate to true as well"), + format!("`if `{rhs_str}` evaluates to true, {lhs_str}` will always evaluate to true as well"), ); } else { span_lint_and_note( @@ -98,7 +98,7 @@ pub(super) fn check<'tcx>( span, "right-hand side of `&&` operator has no effect", Some(and_op.span.to(right_cond.span)), - &format!("`if `{lhs_str}` evaluates to true, {rhs_str}` will always evaluate to true as well"), + format!("`if `{lhs_str}` evaluates to true, {rhs_str}` will always evaluate to true as well"), ); } // We could autofix this error but choose not to, @@ -124,7 +124,7 @@ pub(super) fn check<'tcx>( span, "boolean expression will never evaluate to 'true'", None, - ¬e, + note, ); }; } diff --git a/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs b/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs index f120be13836d8..ca3112ce5c468 100644 --- a/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs +++ b/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs @@ -31,7 +31,7 @@ pub(crate) fn check<'tcx>( cx, DURATION_SUBSEC, expr.span, - &format!("calling `{suggested_fn}()` is more concise than this calculation"), + format!("calling `{suggested_fn}()` is more concise than this calculation"), "try", format!( "{}.{suggested_fn}()", diff --git a/src/tools/clippy/clippy_lints/src/operators/eq_op.rs b/src/tools/clippy/clippy_lints/src/operators/eq_op.rs index 01dd418c38be3..1421893274f5a 100644 --- a/src/tools/clippy/clippy_lints/src/operators/eq_op.rs +++ b/src/tools/clippy/clippy_lints/src/operators/eq_op.rs @@ -24,7 +24,7 @@ pub(crate) fn check_assert<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { cx, EQ_OP, lhs.span.to(rhs.span), - &format!("identical args used in this `{macro_name}!` macro call"), + format!("identical args used in this `{macro_name}!` macro call"), ); } } @@ -41,7 +41,7 @@ pub(crate) fn check<'tcx>( cx, EQ_OP, e.span, - &format!("equal expressions as operands to `{}`", op.as_str()), + format!("equal expressions as operands to `{}`", op.as_str()), |diag| { if let BinOpKind::Ne = op && cx.typeck_results().expr_ty(left).is_floating_point() diff --git a/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs b/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs index 2a933a11e12c0..c56518ac72a46 100644 --- a/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs @@ -113,7 +113,7 @@ fn check_const_operands<'tcx>( cx, MODULO_ARITHMETIC, expr.span, - &format!( + format!( "you are using modulo operator on constants with different signs: `{} % {}`", lhs_operand.string_representation.as_ref().unwrap(), rhs_operand.string_representation.as_ref().unwrap() diff --git a/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs b/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs index a69989e400be4..607930561e0fc 100644 --- a/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs +++ b/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs @@ -30,7 +30,7 @@ pub(super) fn check<'tcx>( cx, PTR_EQ, expr.span, - &format!("use `{top_crate}::ptr::eq` when comparing raw pointers"), + format!("use `{top_crate}::ptr::eq` when comparing raw pointers"), "try", format!("{top_crate}::ptr::eq({left_snip}, {right_snip})"), Applicability::MachineApplicable, diff --git a/src/tools/clippy/clippy_lints/src/operators/self_assignment.rs b/src/tools/clippy/clippy_lints/src/operators/self_assignment.rs index 7c9d5320a3a8b..a932378fbb529 100644 --- a/src/tools/clippy/clippy_lints/src/operators/self_assignment.rs +++ b/src/tools/clippy/clippy_lints/src/operators/self_assignment.rs @@ -14,7 +14,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, lhs: &'tcx cx, SELF_ASSIGNMENT, e.span, - &format!("self-assignment of `{rhs}` to `{lhs}`"), + format!("self-assignment of `{rhs}` to `{lhs}`"), ); } } diff --git a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs index 556c493d36c87..3cbd03a58c554 100644 --- a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs @@ -304,7 +304,7 @@ impl<'tcx> LateLintPass<'tcx> for OptionIfLetElse { cx, OPTION_IF_LET_ELSE, expr.span, - format!("use Option::{} instead of an if let/else", det.method_sugg).as_str(), + format!("use Option::{} instead of an if let/else", det.method_sugg), "try", format!( "{}.{}({}, {})", diff --git a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs index ec03ab0e41ab7..bb4a1de9f77df 100644 --- a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs +++ b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs @@ -206,7 +206,7 @@ impl<'tcx> PassByRefOrValue { cx, TRIVIALLY_COPY_PASS_BY_REF, input.span, - &format!( + format!( "this argument ({size} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)", self.ref_min_size ), @@ -236,7 +236,7 @@ impl<'tcx> PassByRefOrValue { cx, LARGE_TYPES_PASSED_BY_VALUE, input.span, - &format!( + format!( "this argument ({size} byte) is passed by value, but might be more efficient if passed by reference (limit: {} byte)", self.value_max_size ), diff --git a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs index 127801de7defa..44db061b8beed 100644 --- a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs +++ b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs @@ -1,5 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; -use rustc_hir::{intravisit, Body, Expr, ExprKind, FnDecl, LetExpr, LocalSource, Mutability, Pat, PatKind, Stmt, StmtKind}; +use rustc_hir::{ + intravisit, Body, Expr, ExprKind, FnDecl, LetExpr, LocalSource, Mutability, Pat, PatKind, Stmt, StmtKind, +}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; @@ -138,7 +140,7 @@ fn apply_lint(cx: &LateContext<'_>, pat: &Pat<'_>, deref_possible: DerefPossible span, "type of pattern does not match the expression type", None, - &format!( + format!( "{}explicitly match against a `{}` pattern and adjust the enclosed variable bindings", match (deref_possible, level) { (DerefPossible::Possible, Level::Top) => "use `*` to dereference the match expression or ", diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index 896c99a710461..83b32000a9f97 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -177,7 +177,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { ) .filter(|arg| arg.mutability() == Mutability::Not) { - span_lint_hir_and_then(cx, PTR_ARG, arg.emission_id, arg.span, &arg.build_msg(), |diag| { + span_lint_hir_and_then(cx, PTR_ARG, arg.emission_id, arg.span, arg.build_msg(), |diag| { diag.span_suggestion( arg.span, "change this to", @@ -237,7 +237,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { let results = check_ptr_arg_usage(cx, body, &lint_args); for (result, args) in results.iter().zip(lint_args.iter()).filter(|(r, _)| !r.skip) { - span_lint_hir_and_then(cx, PTR_ARG, args.emission_id, args.span, &args.build_msg(), |diag| { + span_lint_hir_and_then(cx, PTR_ARG, args.emission_id, args.span, args.build_msg(), |diag| { diag.multipart_suggestion( "change this to", iter::once((args.span, format!("{}{}", args.ref_prefix, args.deref_ty.display(cx)))) @@ -628,7 +628,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: } }, ExprKind::MethodCall(name, self_arg, expr_args, _) => { - let i = std::iter::once(self_arg) + let i = iter::once(self_arg) .chain(expr_args.iter()) .position(|arg| arg.hir_id == child_id) .unwrap_or(0); diff --git a/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs b/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs index ff8ec2ad57c39..7c82895d60919 100644 --- a/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs +++ b/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs @@ -64,13 +64,13 @@ impl<'tcx> LateLintPass<'tcx> for PtrOffsetWithCast { cx, PTR_OFFSET_WITH_CAST, expr.span, - &msg, + msg, "try", sugg, Applicability::MachineApplicable, ); } else { - span_lint(cx, PTR_OFFSET_WITH_CAST, expr.span, &msg); + span_lint(cx, PTR_OFFSET_WITH_CAST, expr.span, msg); } } } diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index f1db571e1137a..927c6f1d519e8 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -4,7 +4,7 @@ use clippy_config::msrvs::Msrv; use clippy_config::types::MatchLintBehaviour; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{ eq_expr_value, higher, in_constant, is_else_clause, is_lint_allowed, is_path_lang_item, is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt, @@ -14,7 +14,8 @@ use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::{ - BindingAnnotation, Block, ByRef, Expr, ExprKind, LetStmt, Node, PatKind, PathSegment, QPath, Stmt, StmtKind, + BindingAnnotation, Block, Body, ByRef, Expr, ExprKind, LetStmt, Mutability, Node, PatKind, PathSegment, QPath, + Stmt, StmtKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; @@ -109,12 +110,31 @@ fn find_let_else_ret_expression<'hir>(block: &'hir Block<'hir>) -> Option<&'hir } fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) { + /// Make sure the init expr implements try trait so a valid suggestion could be given. + /// + /// Because the init expr could have the type of `&Option` which does not implements `Try`. + /// + /// NB: This conveniently prevents the cause of + /// issue [#12412](https://github.com/rust-lang/rust-clippy/issues/12412), + /// since accessing an `Option` field from a borrowed struct requires borrow, such as + /// `&some_struct.opt`, which is type of `&Option`. And we can't suggest `&some_struct.opt?` + /// or `(&some_struct.opt)?` since the first one has different semantics and the later does + /// not implements `Try`. + fn init_expr_can_use_question_mark(cx: &LateContext<'_>, init_expr: &Expr<'_>) -> bool { + let init_ty = cx.typeck_results().expr_ty_adjusted(init_expr); + cx.tcx + .lang_items() + .try_trait() + .map_or(false, |did| implements_trait(cx, init_ty, did, &[])) + } + if let StmtKind::Let(LetStmt { pat, init: Some(init_expr), els: Some(els), .. }) = stmt.kind + && init_expr_can_use_question_mark(cx, init_expr) && let Some(ret) = find_let_else_ret_expression(els) && let Some(inner_pat) = pat_and_expr_can_be_question_mark(cx, pat, ret) && !span_contains_comment(cx.tcx.sess.source_map(), els.span) @@ -283,9 +303,13 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: let mut applicability = Applicability::MachineApplicable; let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability); let requires_semi = matches!(cx.tcx.parent_hir_node(expr.hir_id), Node::Stmt(_)); + let method_call_str = match by_ref { + ByRef::Yes(Mutability::Mut) => ".as_mut()", + ByRef::Yes(Mutability::Not) => ".as_ref()", + ByRef::No => "", + }; let sugg = format!( - "{receiver_str}{}?{}", - if by_ref == ByRef::Yes { ".as_ref()" } else { "" }, + "{receiver_str}{method_call_str}?{}", if requires_semi { ";" } else { "" } ); span_lint_and_sugg( @@ -306,9 +330,9 @@ impl QuestionMark { } } -fn is_try_block(cx: &LateContext<'_>, bl: &rustc_hir::Block<'_>) -> bool { +fn is_try_block(cx: &LateContext<'_>, bl: &Block<'_>) -> bool { if let Some(expr) = bl.expr - && let rustc_hir::ExprKind::Call(callee, _) = expr.kind + && let ExprKind::Call(callee, _) = expr.kind { is_path_lang_item(cx, callee, LangItem::TryTraitFromOutput) } else { @@ -337,7 +361,7 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark { } } - fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx rustc_hir::Block<'tcx>) { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { if is_try_block(cx, block) { *self .try_block_depth_stack @@ -346,15 +370,15 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark { } } - fn check_body(&mut self, _: &LateContext<'tcx>, _: &'tcx rustc_hir::Body<'tcx>) { + fn check_body(&mut self, _: &LateContext<'tcx>, _: &'tcx Body<'tcx>) { self.try_block_depth_stack.push(0); } - fn check_body_post(&mut self, _: &LateContext<'tcx>, _: &'tcx rustc_hir::Body<'tcx>) { + fn check_body_post(&mut self, _: &LateContext<'tcx>, _: &'tcx Body<'tcx>) { self.try_block_depth_stack.pop(); } - fn check_block_post(&mut self, cx: &LateContext<'tcx>, block: &'tcx rustc_hir::Block<'tcx>) { + fn check_block_post(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { if is_try_block(cx, block) { *self .try_block_depth_stack diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs index 6b54258dd617d..186e548d37304 100644 --- a/src/tools/clippy/clippy_lints/src/ranges.rs +++ b/src/tools/clippy/clippy_lints/src/ranges.rs @@ -242,7 +242,7 @@ fn check_possible_range_contains( cx, MANUAL_RANGE_CONTAINS, span, - &format!("manual `{range_type}::contains` implementation"), + format!("manual `{range_type}::contains` implementation"), "use", format!("({lo}{space}{range_op}{hi}).contains(&{name})"), applicability, @@ -272,7 +272,7 @@ fn check_possible_range_contains( cx, MANUAL_RANGE_CONTAINS, span, - &format!("manual `!{range_type}::contains` implementation"), + format!("manual `!{range_type}::contains` implementation"), "use", format!("!({lo}{space}{range_op}{hi}).contains(&{name})"), applicability, diff --git a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs index 435899ddaa788..2863eb190d341 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs @@ -56,7 +56,7 @@ impl ReturnVisitor { impl<'tcx> Visitor<'tcx> for ReturnVisitor { fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) { - if let hir::ExprKind::Ret(_) | hir::ExprKind::Match(.., hir::MatchSource::TryDesugar(_)) = ex.kind { + if let ExprKind::Ret(_) | ExprKind::Match(.., hir::MatchSource::TryDesugar(_)) = ex.kind { self.found_return = true; } else { hir_visit::walk_expr(self, ex); @@ -68,7 +68,7 @@ impl<'tcx> Visitor<'tcx> for ReturnVisitor { /// Returns true for `async || whatever_expression`, but false for `|| async { whatever_expression /// }`. fn is_async_closure(body: &hir::Body<'_>) -> bool { - if let hir::ExprKind::Closure(innermost_closure_generated_by_desugar) = body.value.kind + if let ExprKind::Closure(innermost_closure_generated_by_desugar) = body.value.kind // checks whether it is `async || whatever_expression` && let ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, CoroutineSource::Closure)) = innermost_closure_generated_by_desugar.kind @@ -99,7 +99,7 @@ fn find_innermost_closure<'tcx>( )> { let mut data = None; - while let hir::ExprKind::Closure(closure) = expr.kind + while let ExprKind::Closure(closure) = expr.kind && let body = cx.tcx.hir().body(closure.body) && { let mut visitor = ReturnVisitor::new(); @@ -137,7 +137,7 @@ fn get_parent_call_exprs<'tcx>( ) -> (&'tcx hir::Expr<'tcx>, usize) { let mut depth = 1; while let Some(parent) = get_parent_expr(cx, expr) - && let hir::ExprKind::Call(recv, _) = parent.kind + && let ExprKind::Call(recv, _) = parent.kind && expr.span == recv.span { expr = parent; @@ -152,13 +152,13 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { return; } - if let hir::ExprKind::Call(recv, _) = expr.kind + if let ExprKind::Call(recv, _) = expr.kind // don't lint if the receiver is a call, too. // we do this in order to prevent linting multiple times; consider: // `(|| || 1)()()` // ^^ we only want to lint for this call (but we walk up the calls to consider both calls). // without this check, we'd end up linting twice. - && !matches!(recv.kind, hir::ExprKind::Call(..)) + && !matches!(recv.kind, ExprKind::Call(..)) // Check if `recv` comes from a macro expansion. If it does, make sure that it's an expansion that is // the same as the one the call is in. // For instance, let's assume `x!()` returns a closure: @@ -185,7 +185,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { Sugg::hir_with_context(cx, body, full_expr.span.ctxt(), "..", &mut applicability); if coroutine_kind.is_async() - && let hir::ExprKind::Closure(closure) = body.kind + && let ExprKind::Closure(closure) = body.kind { // Like `async fn`, async closures are wrapped in an additional block // to move all of the closure's arguments into the future. @@ -202,7 +202,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { }; // `async x` is a syntax error, so it becomes `async { x }` - if !matches!(body_expr.kind, hir::ExprKind::Block(_, _)) { + if !matches!(body_expr.kind, ExprKind::Block(_, _)) { hint = hint.blockify(); } @@ -210,7 +210,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { } let is_in_fn_call_arg = if let Node::Expr(expr) = cx.tcx.parent_hir_node(expr.hir_id) { - matches!(expr.kind, hir::ExprKind::Call(_, _)) + matches!(expr.kind, ExprKind::Call(_, _)) } else { false }; @@ -238,12 +238,12 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { path: &'tcx hir::Path<'tcx>, count: usize, } - impl<'a, 'tcx> hir_visit::Visitor<'tcx> for ClosureUsageCount<'a, 'tcx> { + impl<'a, 'tcx> Visitor<'tcx> for ClosureUsageCount<'a, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { - if let hir::ExprKind::Call(closure, _) = expr.kind - && let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = closure.kind + if let ExprKind::Call(closure, _) = expr.kind + && let ExprKind::Path(hir::QPath::Resolved(_, path)) = closure.kind && self.path.segments[0].ident == path.segments[0].ident && self.path.res == path.res { @@ -263,13 +263,13 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { for w in block.stmts.windows(2) { if let hir::StmtKind::Let(local) = w[0].kind - && let Option::Some(t) = local.init - && let hir::ExprKind::Closure { .. } = t.kind + && let Some(t) = local.init + && let ExprKind::Closure { .. } = t.kind && let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind && let hir::StmtKind::Semi(second) = w[1].kind - && let hir::ExprKind::Assign(_, call, _) = second.kind - && let hir::ExprKind::Call(closure, _) = call.kind - && let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = closure.kind + && let ExprKind::Assign(_, call, _) = second.kind + && let ExprKind::Call(closure, _) = call.kind + && let ExprKind::Path(hir::QPath::Resolved(_, path)) = closure.kind && ident == path.segments[0].ident && count_closure_usage(cx, block, path) == 1 { diff --git a/src/tools/clippy/clippy_lints/src/redundant_locals.rs b/src/tools/clippy/clippy_lints/src/redundant_locals.rs index 0f579f779dfd5..7202266deeb23 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_locals.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_locals.rs @@ -77,9 +77,9 @@ impl<'tcx> LateLintPass<'tcx> for RedundantLocals { cx, REDUNDANT_LOCALS, local.span, - &format!("redundant redefinition of a binding `{ident}`"), + format!("redundant redefinition of a binding `{ident}`"), Some(binding_pat.span), - &format!("`{ident}` is initially defined here"), + format!("`{ident}` is initially defined here"), ); } } diff --git a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs index 0e43e4a7ee530..1b557730ecade 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs @@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { cx, REDUNDANT_PUB_CRATE, span, - &format!("pub(crate) {descr} inside private module"), + format!("pub(crate) {descr} inside private module"), |diag| { diag.span_suggestion( item.vis_span, diff --git a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs index 07b604f2326ba..136e7db83bd69 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs @@ -48,7 +48,7 @@ impl_lint_pass!(RedundantStaticLifetimes => [REDUNDANT_STATIC_LIFETIMES]); impl RedundantStaticLifetimes { // Recursively visit types - fn visit_type(ty: &Ty, cx: &EarlyContext<'_>, reason: &str) { + fn visit_type(ty: &Ty, cx: &EarlyContext<'_>, reason: &'static str) { match ty.kind { // Be careful of nested structures (arrays and tuples) TyKind::Array(ref ty, _) | TyKind::Slice(ref ty) => { diff --git a/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs b/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs index 96f6f0ec36fce..11b95ee3a54ca 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs @@ -59,7 +59,7 @@ fn is_same_type<'tcx>(cx: &LateContext<'tcx>, ty_resolved_path: hir::def::Res, f fn func_hir_id_to_func_ty<'tcx>(cx: &LateContext<'tcx>, hir_id: hir::hir_id::HirId) -> Option> { if let Some((defkind, func_defid)) = cx.typeck_results().type_dependent_def(hir_id) - && defkind == hir::def::DefKind::AssocFn + && defkind == DefKind::AssocFn && let Some(init_ty) = cx.tcx.type_of(func_defid).no_bound_vars() { Some(init_ty) diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs index 687bad35a3685..e925ec0271cf3 100644 --- a/src/tools/clippy/clippy_lints/src/regex.rs +++ b/src/tools/clippy/clippy_lints/src/regex.rs @@ -134,13 +134,13 @@ fn lint_syntax_error(cx: &LateContext<'_>, error: ®ex_syntax::Error, unescape vec![convert_span(primary)] }; - span_lint(cx, INVALID_REGEX, spans, &format!("regex syntax error: {kind}")); + span_lint(cx, INVALID_REGEX, spans, format!("regex syntax error: {kind}")); } else { span_lint_and_help( cx, INVALID_REGEX, base, - &error.to_string(), + error.to_string(), None, "consider using a raw string literal: `r\"..\"`", ); @@ -223,7 +223,7 @@ fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { span_lint_and_help(cx, TRIVIAL_REGEX, expr.span, "trivial regex", None, repl); } }, - Err(e) => span_lint(cx, INVALID_REGEX, expr.span, &e.to_string()), + Err(e) => span_lint(cx, INVALID_REGEX, expr.span, e.to_string()), } } } diff --git a/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs b/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs index fcb79f6d694ee..a358881bf80ee 100644 --- a/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs +++ b/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs @@ -55,7 +55,7 @@ fn emit_lint(cx: &LateContext<'_>, span: Span, kind: &str, note: &'static str, s cx, REPEAT_VEC_WITH_CAPACITY, span, - &format!("repeating `Vec::with_capacity` using `{kind}`, which does not retain capacity"), + format!("repeating `Vec::with_capacity` using `{kind}`, which does not retain capacity"), |diag| { diag.note(note); diag.span_suggestion_verbose(span, sugg_msg, sugg, Applicability::MaybeIncorrect); diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs index 8bc24eda46597..e8f9d43810473 100644 --- a/src/tools/clippy/clippy_lints/src/returns.rs +++ b/src/tools/clippy/clippy_lints/src/returns.rs @@ -3,7 +3,7 @@ use clippy_utils::source::{snippet_opt, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::visitors::{for_each_expr_with_closures, Descend}; use clippy_utils::{ - fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res, path_to_local_id, + fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res, path_to_local_id, span_contains_cfg, span_find_starting_semi, }; use core::ops::ControlFlow; @@ -232,6 +232,7 @@ impl<'tcx> LateLintPass<'tcx> for Return { && !in_external_macro(cx.sess(), initexpr.span) && !in_external_macro(cx.sess(), retexpr.span) && !local.span.from_expansion() + && !span_contains_cfg(cx, stmt.span.between(retexpr.span)) { span_lint_hir_and_then( cx, diff --git a/src/tools/clippy/clippy_lints/src/self_named_constructors.rs b/src/tools/clippy/clippy_lints/src/self_named_constructors.rs index 85a2b1a673525..23b47606f8aa2 100644 --- a/src/tools/clippy/clippy_lints/src/self_named_constructors.rs +++ b/src/tools/clippy/clippy_lints/src/self_named_constructors.rs @@ -81,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for SelfNamedConstructors { cx, SELF_NAMED_CONSTRUCTORS, impl_item.span, - &format!("constructor `{}` has the same name as the type", impl_item.ident.name), + format!("constructor `{}` has the same name as the type", impl_item.ident.name), ); } } diff --git a/src/tools/clippy/clippy_lints/src/serde_api.rs b/src/tools/clippy/clippy_lints/src/serde_api.rs index b4278d879e54e..f1ec91d7affc0 100644 --- a/src/tools/clippy/clippy_lints/src/serde_api.rs +++ b/src/tools/clippy/clippy_lints/src/serde_api.rs @@ -9,7 +9,7 @@ declare_clippy_lint! { /// Checks for misuses of the serde API. /// /// ### Why is this bad? - /// Serde is very finnicky about how its API should be + /// Serde is very finicky about how its API should be /// used, but the type system can't be used to enforce it (yet?). /// /// ### Example diff --git a/src/tools/clippy/clippy_lints/src/shadow.rs b/src/tools/clippy/clippy_lints/src/shadow.rs index d98b37bda355e..9db08acb03b21 100644 --- a/src/tools/clippy/clippy_lints/src/shadow.rs +++ b/src/tools/clippy/clippy_lints/src/shadow.rs @@ -194,7 +194,7 @@ fn lint_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, shadowed: HirId, span: Span) cx, lint, span, - &msg, + msg, Some(cx.tcx.hir().span(shadowed)), "previous binding is here", ); diff --git a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs index 95b4a11a78347..0a9a3c6307a70 100644 --- a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs +++ b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs @@ -122,7 +122,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit { cx, SINGLE_RANGE_IN_VEC_INIT, span, - &format!("{suggested_type} of `Range` that is only one element"), + format!("{suggested_type} of `Range` that is only one element"), |diag| { if should_emit_every_value { diag.span_suggestion( 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 ff8e8fe702176..8a9f02b6dcb1e 100644 --- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs @@ -196,7 +196,7 @@ impl SlowVectorInit { }; } - fn emit_lint(cx: &LateContext<'_>, slow_fill: &Expr<'_>, vec_alloc: &VecAllocation<'_>, msg: &str) { + fn emit_lint(cx: &LateContext<'_>, slow_fill: &Expr<'_>, vec_alloc: &VecAllocation<'_>, msg: &'static str) { let len_expr = Sugg::hir( cx, match vec_alloc.size_expr { diff --git a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs index cf839941123d1..926c56332cc19 100644 --- a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs +++ b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs @@ -128,8 +128,8 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { cx, lint, first_segment.ident.span, - &format!("used import from `{used_mod}` instead of `{replace_with}`"), - &format!("consider importing the item from `{replace_with}`"), + format!("used import from `{used_mod}` instead of `{replace_with}`"), + format!("consider importing the item from `{replace_with}`"), replace_with.to_string(), Applicability::MachineApplicable, ); diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index 13ae1ff52ddfe..b3c729dacdd93 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -495,8 +495,8 @@ impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace { cx, TRIM_SPLIT_WHITESPACE, trim_span.with_hi(split_ws_span.lo()), - &format!("found call to `str::{trim_fn_name}` before `str::split_whitespace`"), - &format!("remove `{trim_fn_name}()`"), + format!("found call to `str::{trim_fn_name}` before `str::split_whitespace`"), + format!("remove `{trim_fn_name}()`"), String::new(), Applicability::MachineApplicable, ); diff --git a/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs index 8eab3f5874e18..3f030b8033187 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs @@ -83,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { cx, lint, binop.span, - &format!( + format!( "suspicious use of `{}` in `{}` impl", binop.node.as_str(), cx.tcx.item_name(trait_id) diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs index be590aede158d..93bad86580975 100644 --- a/src/tools/clippy/clippy_lints/src/swap.rs +++ b/src/tools/clippy/clippy_lints/src/swap.rs @@ -1,10 +1,16 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::snippet_with_context; +use clippy_utils::source::{snippet_indent, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; + use clippy_utils::{can_mut_borrow_both, eq_expr_value, in_constant, std_or_core}; +use itertools::Itertools; + +use rustc_hir::intravisit::{walk_expr, Visitor}; + +use crate::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind}; +use rustc_hir::{BinOpKind, Block, Expr, ExprKind, LetStmt, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; @@ -80,7 +86,17 @@ impl<'tcx> LateLintPass<'tcx> for Swap { } } -fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, span: Span, is_xor_based: bool) { +#[allow(clippy::too_many_arguments)] +fn generate_swap_warning<'tcx>( + block: &'tcx Block<'tcx>, + cx: &LateContext<'tcx>, + e1: &'tcx Expr<'tcx>, + e2: &'tcx Expr<'tcx>, + rhs1: &'tcx Expr<'tcx>, + rhs2: &'tcx Expr<'tcx>, + span: Span, + is_xor_based: bool, +) { let ctxt = span.ctxt(); let mut applicability = Applicability::MachineApplicable; @@ -99,14 +115,25 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa || is_type_diagnostic_item(cx, ty, sym::VecDeque) { let slice = Sugg::hir_with_applicability(cx, lhs1, "", &mut applicability); + span_lint_and_sugg( cx, MANUAL_SWAP, span, - &format!("this looks like you are swapping elements of `{slice}` manually"), + format!("this looks like you are swapping elements of `{slice}` manually"), "try", format!( - "{}.swap({}, {});", + "{}{}.swap({}, {});", + IndexBinding { + block, + swap1_idx: idx1, + swap2_idx: idx2, + suggest_span: span, + cx, + ctxt, + applicability: &mut applicability, + } + .snippet_index_bindings(&[idx1, idx2, rhs1, rhs2]), slice.maybe_par(), snippet_with_context(cx, idx1.span, ctxt, "..", &mut applicability).0, snippet_with_context(cx, idx2.span, ctxt, "..", &mut applicability).0, @@ -126,7 +153,7 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa cx, MANUAL_SWAP, span, - &format!("this looks like you are swapping `{first}` and `{second}` manually"), + format!("this looks like you are swapping `{first}` and `{second}` manually"), |diag| { diag.span_suggestion( span, @@ -142,7 +169,7 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa } /// Implementation of the `MANUAL_SWAP` lint. -fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) { +fn check_manual_swap<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { if in_constant(cx, block.hir_id) { return; } @@ -160,10 +187,10 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) { // bar() = t; && let StmtKind::Semi(second) = s3.kind && let ExprKind::Assign(lhs2, rhs2, _) = second.kind - && let ExprKind::Path(QPath::Resolved(None, rhs2)) = rhs2.kind - && rhs2.segments.len() == 1 + && let ExprKind::Path(QPath::Resolved(None, rhs2_path)) = rhs2.kind + && rhs2_path.segments.len() == 1 - && ident.name == rhs2.segments[0].ident.name + && ident.name == rhs2_path.segments[0].ident.name && eq_expr_value(cx, tmp_init, lhs1) && eq_expr_value(cx, rhs1, lhs2) @@ -174,7 +201,7 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) { && second.span.ctxt() == ctxt { let span = s1.span.to(s3.span); - generate_swap_warning(cx, lhs1, lhs2, span, false); + generate_swap_warning(block, cx, lhs1, lhs2, rhs1, rhs2, span, false); } } } @@ -201,7 +228,7 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) { cx, ALMOST_SWAPPED, span, - &format!("this looks like you are trying to swap `{lhs_sugg}` and `{rhs_sugg}`"), + format!("this looks like you are trying to swap `{lhs_sugg}` and `{rhs_sugg}`"), |diag| { diag.span_suggestion( span, @@ -254,7 +281,7 @@ fn parse<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(ExprOrIdent<'hir>, &'a Expr< } /// Implementation of the xor case for `MANUAL_SWAP` lint. -fn check_xor_swap(cx: &LateContext<'_>, block: &Block<'_>) { +fn check_xor_swap<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { for [s1, s2, s3] in block.stmts.array_windows::<3>() { let ctxt = s1.span.ctxt(); if let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(s1, ctxt) @@ -268,7 +295,7 @@ fn check_xor_swap(cx: &LateContext<'_>, block: &Block<'_>) { && s3.span.ctxt() == ctxt { let span = s1.span.to(s3.span); - generate_swap_warning(cx, lhs0, rhs0, span, true); + generate_swap_warning(block, cx, lhs0, rhs0, rhs1, rhs2, span, true); }; } } @@ -294,3 +321,130 @@ fn extract_sides_of_xor_assign<'a, 'hir>( None } } + +struct IndexBinding<'a, 'tcx> { + block: &'a Block<'a>, + swap1_idx: &'a Expr<'a>, + swap2_idx: &'a Expr<'a>, + suggest_span: Span, + cx: &'a LateContext<'tcx>, + ctxt: SyntaxContext, + applicability: &'a mut Applicability, +} + +impl<'a, 'tcx> IndexBinding<'a, 'tcx> { + fn snippet_index_bindings(&mut self, exprs: &[&'tcx Expr<'tcx>]) -> String { + let mut bindings = FxHashSet::default(); + for expr in exprs { + bindings.insert(self.snippet_index_binding(expr)); + } + bindings.into_iter().join("") + } + + fn snippet_index_binding(&mut self, expr: &'tcx Expr<'tcx>) -> String { + match expr.kind { + ExprKind::Binary(_, lhs, rhs) => { + if matches!(lhs.kind, ExprKind::Lit(_)) && matches!(rhs.kind, ExprKind::Lit(_)) { + return String::new(); + } + let lhs_snippet = self.snippet_index_binding(lhs); + let rhs_snippet = self.snippet_index_binding(rhs); + format!("{lhs_snippet}{rhs_snippet}") + }, + ExprKind::Path(QPath::Resolved(_, path)) => { + let init = self.cx.expr_or_init(expr); + + let Some(first_segment) = path.segments.first() else { + return String::new(); + }; + if !self.suggest_span.contains(init.span) || !self.is_used_other_than_swapping(first_segment.ident) { + return String::new(); + } + + let init_str = snippet_with_context(self.cx, init.span, self.ctxt, "", self.applicability) + .0 + .to_string(); + let indent_str = snippet_indent(self.cx, init.span); + let indent_str = indent_str.as_deref().unwrap_or(""); + + format!("let {} = {init_str};\n{indent_str}", first_segment.ident) + }, + _ => String::new(), + } + } + + fn is_used_other_than_swapping(&mut self, idx_ident: Ident) -> bool { + if Self::is_used_slice_indexed(self.swap1_idx, idx_ident) + || Self::is_used_slice_indexed(self.swap2_idx, idx_ident) + { + return true; + } + self.is_used_after_swap(idx_ident) + } + + 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, + }; + + for stmt in self.block.stmts { + match stmt.kind { + StmtKind::Expr(expr) | StmtKind::Semi(expr) => v.visit_expr(expr), + StmtKind::Let(LetStmt { ref init, .. }) => { + if let Some(init) = init.as_ref() { + v.visit_expr(init); + } + }, + StmtKind::Item(_) => {}, + } + } + + v.found_used + } + + fn is_used_slice_indexed(swap_index: &Expr<'_>, idx_ident: Ident) -> bool { + match swap_index.kind { + ExprKind::Binary(_, lhs, rhs) => { + if matches!(lhs.kind, ExprKind::Lit(_)) && matches!(rhs.kind, ExprKind::Lit(_)) { + return false; + } + Self::is_used_slice_indexed(lhs, idx_ident) || Self::is_used_slice_indexed(rhs, idx_ident) + }, + ExprKind::Path(QPath::Resolved(_, path)) => { + path.segments.first().map_or(false, |idx| idx.ident == idx_ident) + }, + _ => false, + } + } +} + +struct IndexBindingVisitor { + idx: Ident, + suggest_span: Span, + found_used: bool, +} + +impl<'tcx> Visitor<'tcx> for IndexBindingVisitor { + fn visit_path_segment(&mut self, path_segment: &'tcx rustc_hir::PathSegment<'tcx>) -> Self::Result { + if path_segment.ident == self.idx { + self.found_used = true; + } + } + + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) -> Self::Result { + if expr.span.hi() <= self.suggest_span.hi() { + return; + } + + match expr.kind { + ExprKind::Path(QPath::Resolved(_, path)) => { + for segment in path.segments { + self.visit_path_segment(segment); + } + }, + _ => walk_expr(self, expr), + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/thread_local_initializer_can_be_made_const.rs b/src/tools/clippy/clippy_lints/src/thread_local_initializer_can_be_made_const.rs index f8bdb866ca391..c1e24674e3e8c 100644 --- a/src/tools/clippy/clippy_lints/src/thread_local_initializer_can_be_made_const.rs +++ b/src/tools/clippy/clippy_lints/src/thread_local_initializer_can_be_made_const.rs @@ -59,7 +59,7 @@ impl_lint_pass!(ThreadLocalInitializerCanBeMadeConst => [THREAD_LOCAL_INITIALIZE #[inline] fn is_thread_local_initializer( cx: &LateContext<'_>, - fn_kind: rustc_hir::intravisit::FnKind<'_>, + fn_kind: intravisit::FnKind<'_>, span: rustc_span::Span, ) -> Option { let macro_def_id = span.source_callee()?.macro_def_id?; @@ -85,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for ThreadLocalInitializerCanBeMadeConst { fn check_fn( &mut self, cx: &LateContext<'tcx>, - fn_kind: rustc_hir::intravisit::FnKind<'tcx>, + fn_kind: intravisit::FnKind<'tcx>, _: &'tcx rustc_hir::FnDecl<'tcx>, body: &'tcx rustc_hir::Body<'tcx>, span: rustc_span::Span, 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 cbdf31c933647..462084e96a864 100644 --- a/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs +++ b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs @@ -44,7 +44,7 @@ impl<'tcx> LateLintPass<'tcx> for TrailingEmptyArray { item.span, "trailing zero-sized array in a struct which is not marked with a `repr` attribute", None, - &format!( + format!( "consider annotating `{}` with `#[repr(C)]` or another `repr` attribute", cx.tcx.def_path_str(item.owner_id) ), diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs index 768623b5d0347..9468d367a9261 100644 --- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs @@ -293,7 +293,7 @@ impl TraitBounds { p.span, "this type has already been used as a bound predicate", None, - &hint_string, + hint_string, ); } } @@ -420,7 +420,11 @@ fn into_comparable_trait_ref(trait_ref: &TraitRef<'_>) -> ComparableTraitRef { ) } -fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) -> Vec<(ComparableTraitRef, Span)> { +fn rollup_traits( + cx: &LateContext<'_>, + bounds: &[GenericBound<'_>], + msg: &'static str, +) -> Vec<(ComparableTraitRef, Span)> { let mut map = FxHashMap::default(); let mut repeated_res = false; diff --git a/src/tools/clippy/clippy_lints/src/transmute/crosspointer_transmute.rs b/src/tools/clippy/clippy_lints/src/transmute/crosspointer_transmute.rs index 102aee1cb9599..c8f959a9854b4 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/crosspointer_transmute.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/crosspointer_transmute.rs @@ -13,7 +13,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty cx, CROSSPOINTER_TRANSMUTE, e.span, - &format!("transmute from a type (`{from_ty}`) to the type that it points to (`{to_ty}`)"), + format!("transmute from a type (`{from_ty}`) to the type that it points to (`{to_ty}`)"), ); true }, @@ -22,7 +22,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty cx, CROSSPOINTER_TRANSMUTE, e.span, - &format!("transmute from a type (`{from_ty}`) to a pointer to that type (`{to_ty}`)"), + format!("transmute from a type (`{from_ty}`) to a pointer to that type (`{to_ty}`)"), ); true }, diff --git a/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs b/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs new file mode 100644 index 0000000000000..cc6ff1cf3b42d --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs @@ -0,0 +1,87 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use rustc_errors::Applicability; +use rustc_hir::{GenericArg, HirId, LetStmt, Node, Path, TyKind}; +use rustc_lint::LateContext; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::Ty; + +use crate::transmute::MISSING_TRANSMUTE_ANNOTATIONS; + +fn get_parent_local_binding_ty<'tcx>(cx: &LateContext<'tcx>, expr_hir_id: HirId) -> Option> { + let mut parent_iter = cx.tcx.hir().parent_iter(expr_hir_id); + if let Some((_, node)) = parent_iter.next() { + match node { + Node::LetStmt(local) => Some(*local), + Node::Block(_) => { + if let Some((parent_hir_id, Node::Expr(expr))) = parent_iter.next() + && matches!(expr.kind, rustc_hir::ExprKind::Block(_, _)) + { + get_parent_local_binding_ty(cx, parent_hir_id) + } else { + None + } + }, + _ => None, + } + } else { + None + } +} + +fn is_function_block(cx: &LateContext<'_>, expr_hir_id: HirId) -> bool { + let def_id = cx.tcx.hir().enclosing_body_owner(expr_hir_id); + if let Some(body_id) = cx.tcx.hir().maybe_body_owned_by(def_id) { + let body = cx.tcx.hir().body(body_id); + return body.value.peel_blocks().hir_id == expr_hir_id; + } + false +} + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + path: &Path<'tcx>, + from_ty: Ty<'tcx>, + to_ty: Ty<'tcx>, + expr_hir_id: HirId, +) -> bool { + let last = path.segments.last().unwrap(); + if in_external_macro(cx.tcx.sess, last.ident.span) { + // If it comes from a non-local macro, we ignore it. + return false; + } + let args = last.args; + let missing_generic = match args { + Some(args) if !args.args.is_empty() => args.args.iter().any(|arg| match arg { + GenericArg::Infer(_) => true, + GenericArg::Type(ty) => matches!(ty.kind, TyKind::Infer), + _ => false, + }), + _ => true, + }; + if !missing_generic { + return false; + } + // If it's being set as a local variable value... + if let Some(local) = get_parent_local_binding_ty(cx, expr_hir_id) { + // ... which does have type annotations. + if let Some(ty) = local.ty + // If this is a `let x: _ =`, we should lint. + && !matches!(ty.kind, TyKind::Infer) + { + return false; + } + // We check if this transmute is not the only element in the function + } else if is_function_block(cx, expr_hir_id) { + return false; + } + span_lint_and_sugg( + cx, + MISSING_TRANSMUTE_ANNOTATIONS, + last.ident.span.with_hi(path.span.hi()), + "transmute used without annotations", + "consider adding missing annotations", + format!("{}::<{from_ty}, {to_ty}>", last.ident.as_str()), + Applicability::MaybeIncorrect, + ); + true +} diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs index 3c11b48131051..7fa536a1a29d9 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs @@ -1,5 +1,6 @@ mod crosspointer_transmute; mod eager_transmute; +mod missing_transmute_annotations; mod transmute_float_to_int; mod transmute_int_to_bool; mod transmute_int_to_char; @@ -520,6 +521,37 @@ declare_clippy_lint! { "eager evaluation of `transmute`" } +declare_clippy_lint! { + /// ### What it does + /// Checks if transmute calls have all generics specified. + /// + /// ### Why is this bad? + /// If not set, some unexpected output type could be retrieved instead of the expected one, + /// potentially leading to invalid code. + /// + /// This is particularly dangerous in case a seemingly innocent/unrelated change can cause type + /// inference to start inferring a different type. E.g. the transmute is the tail expression of + /// an `if` branch, and a different branches type changes, causing the transmute to silently + /// have a different type, instead of a proper error. + /// + /// ### Example + /// ```no_run + /// # unsafe { + /// let x: i32 = std::mem::transmute([1u16, 2u16]); + /// # } + /// ``` + /// Use instead: + /// ```no_run + /// # unsafe { + /// let x = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); + /// # } + /// ``` + #[clippy::version = "1.77.0"] + pub MISSING_TRANSMUTE_ANNOTATIONS, + suspicious, + "warns if a transmute call doesn't have all generics specified" +} + pub struct Transmute { msrv: Msrv, } @@ -542,6 +574,7 @@ impl_lint_pass!(Transmute => [ TRANSMUTING_NULL, TRANSMUTE_NULL_TO_FN, EAGER_TRANSMUTE, + MISSING_TRANSMUTE_ANNOTATIONS, ]); impl Transmute { #[must_use] @@ -579,6 +612,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { | transmuting_null::check(cx, e, arg, to_ty) | transmute_null_to_fn::check(cx, e, arg, to_ty) | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, &self.msrv) + | missing_transmute_annotations::check(cx, path, from_ty, to_ty, e.hir_id) | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg) diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_float_to_int.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_float_to_int.rs index aef520923e477..ab3bb5e1062d3 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_float_to_int.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_float_to_int.rs @@ -23,7 +23,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_FLOAT_TO_INT, e.span, - &format!("transmute from a `{from_ty}` to a `{to_ty}`"), + format!("transmute from a `{from_ty}` to a `{to_ty}`"), |diag| { let mut sugg = sugg::Sugg::hir(cx, arg, ".."); diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_bool.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_bool.rs index 58227c53de2f1..a719280907795 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_bool.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_bool.rs @@ -23,7 +23,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_INT_TO_BOOL, e.span, - &format!("transmute from a `{from_ty}` to a `bool`"), + format!("transmute from a `{from_ty}` to a `bool`"), |diag| { let arg = sugg::Sugg::hir(cx, arg, ".."); let zero = sugg::Sugg::NonParen(Cow::from("0")); diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_char.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_char.rs index 2a6c248125459..81d10a7d5bde4 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_char.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_char.rs @@ -23,7 +23,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_INT_TO_CHAR, e.span, - &format!("transmute from a `{from_ty}` to a `char`"), + format!("transmute from a `{from_ty}` to a `char`"), |diag| { let Some(top_crate) = std_or_core(cx) else { return }; let arg = sugg::Sugg::hir(cx, arg, ".."); diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_float.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_float.rs index cc3422edbbf13..d51888e30971b 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_float.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_float.rs @@ -22,7 +22,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_INT_TO_FLOAT, e.span, - &format!("transmute from a `{from_ty}` to a `{to_ty}`"), + format!("transmute from a `{from_ty}` to a `{to_ty}`"), |diag| { let arg = sugg::Sugg::hir(cx, arg, ".."); let arg = if let ty::Int(int_ty) = from_ty.kind() { diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs index 97068efd43cd8..234021f0f4797 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs @@ -58,7 +58,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_INT_TO_NON_ZERO, e.span, - &format!("transmute from a `{from_ty}` to a `{nonzero_alias}`"), + format!("transmute from a `{from_ty}` to a `{nonzero_alias}`"), |diag| { let arg = sugg::Sugg::hir(cx, arg, ".."); diag.span_suggestion( diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_num_to_bytes.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_num_to_bytes.rs index 009d5a7c8ae18..88b0ac5a36887 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_num_to_bytes.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_num_to_bytes.rs @@ -31,7 +31,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_NUM_TO_BYTES, e.span, - &format!("transmute from a `{from_ty}` to a `{to_ty}`"), + format!("transmute from a `{from_ty}` to a `{to_ty}`"), |diag| { let arg = sugg::Sugg::hir(cx, arg, ".."); diag.span_suggestion( diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs index cf78709583cf5..eaf927c0005fd 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs @@ -25,7 +25,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_PTR_TO_REF, e.span, - &format!("transmute from a pointer type (`{from_ty}`) to a reference type (`{to_ty}`)"), + format!("transmute from a pointer type (`{from_ty}`) to a reference type (`{to_ty}`)"), |diag| { let arg = sugg::Sugg::hir(cx, arg, ".."); let (deref, cast) = if *mutbl == Mutability::Mut { diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs index 73321c56f3fec..3842c4eb60e82 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs @@ -35,7 +35,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_BYTES_TO_STR, e.span, - &format!("transmute from a `{from_ty}` to a `{to_ty}`"), + format!("transmute from a `{from_ty}` to a `{to_ty}`"), "consider using", if const_context { format!("{top_crate}::str::from_utf8_unchecked{postfix}({snippet})") 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 33c4031fa8759..9c8dd37d53dcd 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 @@ -71,7 +71,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_UNDEFINED_REPR, e.span, - &format!("transmute from `{from_ty_orig}` which has an undefined layout"), + format!("transmute from `{from_ty_orig}` which has an undefined layout"), |diag| { if from_ty_orig.peel_refs() != from_ty.peel_refs() { diag.note(format!("the contained type `{from_ty}` has an undefined layout")); @@ -85,7 +85,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_UNDEFINED_REPR, e.span, - &format!("transmute to `{to_ty_orig}` which has an undefined layout"), + format!("transmute to `{to_ty_orig}` which has an undefined layout"), |diag| { if to_ty_orig.peel_refs() != to_ty.peel_refs() { diag.note(format!("the contained type `{to_ty}` has an undefined layout")); @@ -111,7 +111,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_UNDEFINED_REPR, e.span, - &format!( + format!( "transmute from `{from_ty_orig}` to `{to_ty_orig}`, both of which have an undefined layout" ), |diag| { @@ -140,7 +140,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_UNDEFINED_REPR, e.span, - &format!("transmute from `{from_ty_orig}` which has an undefined layout"), + format!("transmute from `{from_ty_orig}` which has an undefined layout"), |diag| { if from_ty_orig.peel_refs() != from_ty { diag.note(format!("the contained type `{from_ty}` has an undefined layout")); @@ -157,7 +157,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_UNDEFINED_REPR, e.span, - &format!("transmute into `{to_ty_orig}` which has an undefined layout"), + format!("transmute into `{to_ty_orig}` which has an undefined layout"), |diag| { if to_ty_orig.peel_refs() != to_ty { diag.note(format!("the contained type `{to_ty}` has an undefined layout")); diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs index 043c9c8860192..6f5ac625e3575 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs @@ -53,7 +53,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, e.span, - &format!("transmute from `{from_ty}` to `{to_ty}` which could be expressed as a pointer cast instead"), + format!("transmute from `{from_ty}` to `{to_ty}` which could be expressed as a pointer cast instead"), "try", sugg, app, diff --git a/src/tools/clippy/clippy_lints/src/transmute/unsound_collection_transmute.rs b/src/tools/clippy/clippy_lints/src/transmute/unsound_collection_transmute.rs index 891fefc17a64b..35e9383076606 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/unsound_collection_transmute.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/unsound_collection_transmute.rs @@ -37,7 +37,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty cx, UNSOUND_COLLECTION_TRANSMUTE, e.span, - &format!("transmute from `{from_ty}` to `{to_ty}` with mismatched layout is unsound"), + format!("transmute from `{from_ty}` to `{to_ty}` with mismatched layout is unsound"), ); true } else { diff --git a/src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs b/src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs index 70628f3d4f414..ec5fb2793f976 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs @@ -21,7 +21,7 @@ pub(super) fn check<'tcx>( cx, USELESS_TRANSMUTE, e.span, - &format!("transmute from a type (`{from_ty}`) to itself"), + format!("transmute from a type (`{from_ty}`) to itself"), ); true }, diff --git a/src/tools/clippy/clippy_lints/src/transmute/wrong_transmute.rs b/src/tools/clippy/clippy_lints/src/transmute/wrong_transmute.rs index ed815884a7640..14a5a308a641d 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/wrong_transmute.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/wrong_transmute.rs @@ -13,7 +13,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty cx, WRONG_TRANSMUTE, e.span, - &format!("transmute from a `{from_ty}` to a pointer"), + format!("transmute from a `{from_ty}` to a pointer"), ); true }, diff --git a/src/tools/clippy/clippy_lints/src/types/box_collection.rs b/src/tools/clippy/clippy_lints/src/types/box_collection.rs index fc3420af02082..9ac73394548c7 100644 --- a/src/tools/clippy/clippy_lints/src/types/box_collection.rs +++ b/src/tools/clippy/clippy_lints/src/types/box_collection.rs @@ -21,9 +21,9 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ cx, BOX_COLLECTION, hir_ty.span, - &format!("you seem to be trying to use `Box<{box_content}>`. Consider using just `{box_content}`"), + format!("you seem to be trying to use `Box<{box_content}>`. Consider using just `{box_content}`"), None, - &format!("`{box_content}` is already on the heap, `Box<{box_content}>` makes an extra allocation"), + format!("`{box_content}` is already on the heap, `Box<{box_content}>` makes an extra allocation"), ); true } else { diff --git a/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs b/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs index 37437cbfbec4a..0801eace4fcfd 100644 --- a/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs +++ b/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs @@ -29,7 +29,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>, qpath: cx, REDUNDANT_ALLOCATION, hir_ty.span, - &format!("usage of `{outer_sym}<{generic_snippet}>`"), + format!("usage of `{outer_sym}<{generic_snippet}>`"), |diag| { diag.span_suggestion(hir_ty.span, "try", format!("{generic_snippet}"), applicability); diag.note(format!( @@ -73,7 +73,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>, qpath: cx, REDUNDANT_ALLOCATION, hir_ty.span, - &format!("usage of `{outer_sym}<{inner_sym}<{generic_snippet}>>`"), + format!("usage of `{outer_sym}<{inner_sym}<{generic_snippet}>>`"), |diag| { diag.span_suggestion( hir_ty.span, @@ -92,7 +92,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>, qpath: cx, REDUNDANT_ALLOCATION, hir_ty.span, - &format!("usage of `{outer_sym}<{inner_sym}<{generic_snippet}>>`"), + format!("usage of `{outer_sym}<{inner_sym}<{generic_snippet}>>`"), |diag| { diag.note(format!( "`{inner_sym}<{generic_snippet}>` is already on the heap, `{outer_sym}<{inner_sym}<{generic_snippet}>>` makes an extra allocation" diff --git a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs index 8764e3006c6b9..0c4e2c91aec51 100644 --- a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs @@ -23,7 +23,7 @@ declare_clippy_lint! { /// implementations. /// /// ### Why is this bad? - /// This is a hard to find infinite recursion that will crash any code. + /// This is a hard to find infinite recursion that will crash any code /// using it. /// /// ### Example diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs index 5fe4b74b3a7a6..cbd1618007700 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -200,7 +200,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { let item_has_safety_comment = item_has_safety_comment(cx, item); match (&item.kind, item_has_safety_comment) { // lint unsafe impl without safety comment - (hir::ItemKind::Impl(impl_), HasSafetyComment::No) if impl_.unsafety == hir::Unsafety::Unsafe => { + (ItemKind::Impl(impl_), HasSafetyComment::No) if impl_.unsafety == hir::Unsafety::Unsafe => { if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, item.hir_id()) && !is_unsafe_from_proc_macro(cx, item.span) { @@ -222,7 +222,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { } }, // lint safe impl with unnecessary safety comment - (hir::ItemKind::Impl(impl_), HasSafetyComment::Yes(pos)) if impl_.unsafety == hir::Unsafety::Normal => { + (ItemKind::Impl(impl_), HasSafetyComment::Yes(pos)) if impl_.unsafety == hir::Unsafety::Normal => { if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) { let (span, help_span) = mk_spans(pos); @@ -236,9 +236,9 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { ); } }, - (hir::ItemKind::Impl(_), _) => {}, + (ItemKind::Impl(_), _) => {}, // const and static items only need a safety comment if their body is an unsafe block, lint otherwise - (&hir::ItemKind::Const(.., body) | &hir::ItemKind::Static(.., body), HasSafetyComment::Yes(pos)) => { + (&ItemKind::Const(.., body) | &ItemKind::Static(.., body), HasSafetyComment::Yes(pos)) => { if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, body.hir_id) { let body = cx.tcx.hir().body(body); if !matches!( @@ -251,7 +251,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { cx, UNNECESSARY_SAFETY_COMMENT, span, - &format!("{} has unnecessary safety comment", item.kind.descr()), + format!("{} has unnecessary safety comment", item.kind.descr()), Some(help_span), "consider removing the safety comment", ); @@ -268,7 +268,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { cx, UNNECESSARY_SAFETY_COMMENT, span, - &format!("{} has unnecessary safety comment", item.kind.descr()), + format!("{} has unnecessary safety comment", item.kind.descr()), Some(help_span), "consider removing the safety comment", ); @@ -338,13 +338,13 @@ fn block_parents_have_safety_comment( accept_comment_above_statement: bool, accept_comment_above_attributes: bool, cx: &LateContext<'_>, - id: hir::HirId, + id: HirId, ) -> bool { let (span, hir_id) = match cx.tcx.parent_hir_node(id) { Node::Expr(expr) => match cx.tcx.parent_hir_node(expr.hir_id) { Node::LetStmt(hir::LetStmt { span, hir_id, .. }) => (*span, *hir_id), Node::Item(hir::Item { - kind: hir::ItemKind::Const(..) | ItemKind::Static(..), + kind: ItemKind::Const(..) | ItemKind::Static(..), span, owner_id, .. @@ -365,7 +365,7 @@ fn block_parents_have_safety_comment( }) | Node::LetStmt(hir::LetStmt { span, hir_id, .. }) => (*span, *hir_id), Node::Item(hir::Item { - kind: hir::ItemKind::Const(..) | ItemKind::Static(..), + kind: ItemKind::Const(..) | ItemKind::Static(..), span, owner_id, .. @@ -605,11 +605,11 @@ fn get_body_search_span(cx: &LateContext<'_>) -> Option { Node::Expr(e) => span = e.span, Node::Block(_) | Node::Arm(_) | Node::Stmt(_) | Node::LetStmt(_) => (), Node::Item(hir::Item { - kind: hir::ItemKind::Const(..) | ItemKind::Static(..), + kind: ItemKind::Const(..) | ItemKind::Static(..), .. }) => maybe_global_var = true, Node::Item(hir::Item { - kind: hir::ItemKind::Mod(_), + kind: ItemKind::Mod(_), span: item_span, .. }) => { diff --git a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs index 729972de6e6f3..214b69dc9250b 100644 --- a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs +++ b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs @@ -153,7 +153,7 @@ impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd { cx, UNIT_RETURN_EXPECTING_ORD, span, - &format!( + format!( "this closure returns \ the unit type which also implements {trait_name}" ), @@ -164,7 +164,7 @@ impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd { cx, UNIT_RETURN_EXPECTING_ORD, span, - &format!( + format!( "this closure returns \ the unit type which also implements {trait_name}" ), diff --git a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs index ffb909d790734..80b661a757c21 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_context; -use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source}; +use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source, is_local_used}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; +use rustc_hir::intravisit::{walk_body, Visitor}; use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, LetStmt, MatchSource, Node, PatKind, QPath, TyKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::{in_external_macro, is_from_async_await}; @@ -75,12 +76,53 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) { let snip = snippet_with_context(cx, expr.span, local.span.ctxt(), "()", &mut app).0; diag.span_suggestion(local.span, "omit the `let` binding", format!("{snip};"), app); } + + if let PatKind::Binding(_, binding_hir_id, ident, ..) = local.pat.kind + && let Some(body_id) = cx.enclosing_body.as_ref() + && let body = cx.tcx.hir().body(*body_id) + && is_local_used(cx, body, binding_hir_id) + { + let identifier = ident.as_str(); + let mut visitor = UnitVariableCollector::new(binding_hir_id); + walk_body(&mut visitor, body); + visitor.spans.into_iter().for_each(|span| { + let msg = + format!("variable `{identifier}` of type `()` can be replaced with explicit `()`"); + diag.span_suggestion(span, msg, "()", Applicability::MachineApplicable); + }); + } }, ); } } } +struct UnitVariableCollector { + id: HirId, + spans: Vec, +} + +impl UnitVariableCollector { + fn new(id: HirId) -> Self { + Self { id, spans: vec![] } + } +} + +/** + * Collect all instances where a variable is used based on its `HirId`. + */ +impl<'tcx> Visitor<'tcx> for UnitVariableCollector { + fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) -> Self::Result { + if let ExprKind::Path(QPath::Resolved(None, path)) = ex.kind + && let Res::Local(id) = path.res + && id == self.id + { + self.spans.push(path.span); + } + rustc_hir::intravisit::walk_expr(self, ex); + } +} + /// Checks sub-expressions which create the value returned by the given expression for whether /// return value inference is needed. This checks through locals to see if they also need inference /// at this point. diff --git a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs index eba7fa7b993c2..afc53e6f32d96 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use rustc_errors::Applicability; -use rustc_hir::{self as hir, Block, Expr, ExprKind, MatchSource, Node, StmtKind}; +use rustc_hir::{Block, Expr, ExprKind, MatchSource, Node, StmtKind}; use rustc_lint::LateContext; use super::{utils, UNIT_ARG}; @@ -19,7 +19,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if is_questionmark_desugar_marked_call(expr) { return; } - if let hir::Node::Expr(parent_expr) = cx.tcx.parent_hir_node(expr.hir_id) + if let Node::Expr(parent_expr) = cx.tcx.parent_hir_node(expr.hir_id) && is_questionmark_desugar_marked_call(parent_expr) { return; @@ -69,7 +69,7 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp cx, UNIT_ARG, expr.span, - &format!("passing {singular}unit value{plural} to a function"), + format!("passing {singular}unit value{plural} to a function"), |db| { let mut or = ""; args_to_recover diff --git a/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs b/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs index d4342ec5169a2..6dcc1195a7051 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs @@ -24,7 +24,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { cx, UNIT_CMP, macro_call.span, - &format!("`{macro_name}` of unit values detected. This will always {result}"), + format!("`{macro_name}` of unit values detected. This will always {result}"), ); } return; @@ -41,7 +41,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { cx, UNIT_CMP, expr.span, - &format!( + format!( "{}-comparison of unit values detected. This will always be {result}", op.as_str() ), diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs index c332cf076ae7d..bfcefb26153fb 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs @@ -88,7 +88,7 @@ impl UnnecessaryBoxReturns { cx, UNNECESSARY_BOX_RETURNS, return_ty_hir.span, - format!("boxed return of the sized type `{boxed_ty}`").as_str(), + format!("boxed return of the sized type `{boxed_ty}`"), |diagnostic| { diagnostic.span_suggestion( return_ty_hir.span, diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs b/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs index 2b0d2d61d207f..8f1eb5019f0fc 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs @@ -65,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor { hir::QPath::LangItem(..) => return, }; match constructor_symbol { - sym::Some | sym::Ok if path.ident.name == rustc_span::sym::map => (), + sym::Some | sym::Ok if path.ident.name == sym::map => (), sym::Err if path.ident.name == sym::map_err => (), _ => return, } @@ -86,7 +86,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor { cx, UNNECESSARY_MAP_ON_CONSTRUCTOR, expr.span, - &format!( + format!( "unnecessary {} on constructor {constructor_snippet}(_)", path.ident.name ), diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs index 9c8b0ae172763..5c7fbbab988bf 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs @@ -156,7 +156,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { ) }; - span_lint_and_then(cx, UNNECESSARY_WRAPS, span, lint_msg.as_str(), |diag| { + span_lint_and_then(cx, UNNECESSARY_WRAPS, span, lint_msg, |diag| { diag.span_suggestion( fn_decl.output.span(), return_type_sugg_msg, 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 d4dd31e11781a..0049de931f4f1 100644 --- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs @@ -215,7 +215,7 @@ macro_rules! always_pat { /// in `alternatives[focus_idx + 1..]`. fn transform_with_focus_on_idx(alternatives: &mut ThinVec>, focus_idx: usize) -> bool { // Extract the kind; we'll need to make some changes in it. - let mut focus_kind = mem::replace(&mut alternatives[focus_idx].kind, PatKind::Wild); + let mut focus_kind = mem::replace(&mut alternatives[focus_idx].kind, Wild); // We'll focus on `alternatives[focus_idx]`, // so we're draining from `alternatives[focus_idx + 1..]`. let start = focus_idx + 1; @@ -232,7 +232,7 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec>, focus_idx: us // In the case of only two patterns, replacement adds net characters. | Ref(_, Mutability::Not) // Dealt with elsewhere. - | Or(_) | Paren(_) => false, + | Or(_) | Paren(_) | Deref(_) => false, // Transform `box x | ... | box y` into `box (x | y)`. // // The cases below until `Slice(...)` deal with *singleton* products. @@ -242,8 +242,6 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec>, focus_idx: us |k| matches!(k, Box(_)), |k| always_pat!(k, Box(p) => p), ), - // FIXME(deref_patterns): Should we merge patterns here? - Deref(_) => false, // Transform `&mut x | ... | &mut y` into `&mut (x | y)`. Ref(target, Mutability::Mut) => extend_with_matching( target, start, alternatives, diff --git a/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs b/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs index 3f2f765f75126..51b3ea93b6dc9 100644 --- a/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs +++ b/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs @@ -65,7 +65,7 @@ fn unsafe_to_safe_check(old_name: Ident, new_name: Ident, cx: &EarlyContext<'_>, cx, UNSAFE_REMOVED_FROM_NAME, span, - &format!("removed `unsafe` from the name of `{old_str}` in use as `{new_str}`"), + format!("removed `unsafe` from the name of `{old_str}` in use as `{new_str}`"), ); } } diff --git a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs index d8c5f1b7382db..448946bd66d51 100644 --- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs +++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs @@ -85,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { if let Some(exp) = block.expr && matches!( exp.kind, - hir::ExprKind::If(_, _, _) | hir::ExprKind::Match(_, _, hir::MatchSource::Normal) + ExprKind::If(_, _, _) | ExprKind::Match(_, _, hir::MatchSource::Normal) ) { check_expr(cx, exp); @@ -130,7 +130,7 @@ fn non_consuming_ok_arm<'a>(cx: &LateContext<'a>, arm: &hir::Arm<'a>) -> bool { fn check_expr<'a>(cx: &LateContext<'a>, expr: &'a hir::Expr<'a>) { match expr.kind { - hir::ExprKind::If(cond, _, _) + ExprKind::If(cond, _, _) if let ExprKind::Let(hir::LetExpr { pat, init, .. }) = cond.kind && is_ok_wild_or_dotdot_pattern(cx, pat) && let Some(op) = should_lint(cx, init) => @@ -140,7 +140,7 @@ fn check_expr<'a>(cx: &LateContext<'a>, expr: &'a hir::Expr<'a>) { // we will capture only the case where the match is Ok( ) or Err( ) // prefer to match the minimum possible, and expand later if needed // to avoid false positives on something as used as this - hir::ExprKind::Match(expr, [arm1, arm2], hir::MatchSource::Normal) if let Some(op) = should_lint(cx, expr) => { + ExprKind::Match(expr, [arm1, arm2], hir::MatchSource::Normal) if let Some(op) = should_lint(cx, expr) => { if non_consuming_ok_arm(cx, arm1) && non_consuming_err_arm(cx, arm2) { emit_lint(cx, expr.span, expr.hir_id, op, &[arm1.pat.span]); } @@ -148,7 +148,7 @@ fn check_expr<'a>(cx: &LateContext<'a>, expr: &'a hir::Expr<'a>) { emit_lint(cx, expr.span, expr.hir_id, op, &[arm2.pat.span]); } }, - hir::ExprKind::Match(_, _, hir::MatchSource::Normal) => {}, + ExprKind::Match(_, _, hir::MatchSource::Normal) => {}, _ if let Some(op) = should_lint(cx, expr) => { emit_lint(cx, expr.span, expr.hir_id, op, &[]); }, @@ -201,7 +201,7 @@ fn is_unreachable_or_panic(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { } fn unpack_call_chain<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { - while let hir::ExprKind::MethodCall(path, receiver, ..) = expr.kind { + while let ExprKind::MethodCall(path, receiver, ..) = expr.kind { if matches!( path.ident.as_str(), "unwrap" | "expect" | "unwrap_or" | "unwrap_or_else" | "ok" | "is_ok" | "is_err" | "or_else" | "or" @@ -215,10 +215,10 @@ fn unpack_call_chain<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { } fn unpack_try<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { - while let hir::ExprKind::Call(func, [ref arg_0, ..]) = expr.kind + while let ExprKind::Call(func, [ref arg_0, ..]) = expr.kind && matches!( func.kind, - hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryTraitBranch, ..)) + ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryTraitBranch, ..)) ) { expr = arg_0; @@ -227,7 +227,7 @@ fn unpack_try<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { } fn unpack_match<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { - while let hir::ExprKind::Match(res, _, _) = expr.kind { + while let ExprKind::Match(res, _, _) = expr.kind { expr = res; } expr @@ -236,11 +236,11 @@ fn unpack_match<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { /// If `expr` is an (e).await, return the inner expression "e" that's being /// waited on. Otherwise return None. fn unpack_await<'a>(expr: &'a hir::Expr<'a>) -> &hir::Expr<'a> { - if let hir::ExprKind::Match(expr, _, hir::MatchSource::AwaitDesugar) = expr.kind { - if let hir::ExprKind::Call(func, [ref arg_0, ..]) = expr.kind { + if let ExprKind::Match(expr, _, hir::MatchSource::AwaitDesugar) = expr.kind { + if let ExprKind::Call(func, [ref arg_0, ..]) = expr.kind { if matches!( func.kind, - hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoFutureIntoFuture, ..)) + ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoFutureIntoFuture, ..)) ) { return arg_0; } @@ -251,7 +251,7 @@ fn unpack_await<'a>(expr: &'a hir::Expr<'a>) -> &hir::Expr<'a> { /// Check whether the current expr is a function call for an IO operation fn check_io_mode(cx: &LateContext<'_>, call: &hir::Expr<'_>) -> Option { - let hir::ExprKind::MethodCall(path, ..) = call.kind else { + let ExprKind::MethodCall(path, ..) = call.kind else { return None; }; diff --git a/src/tools/clippy/clippy_lints/src/unused_rounding.rs b/src/tools/clippy/clippy_lints/src/unused_rounding.rs index d5ca844b9e24b..3e5afec541c49 100644 --- a/src/tools/clippy/clippy_lints/src/unused_rounding.rs +++ b/src/tools/clippy/clippy_lints/src/unused_rounding.rs @@ -55,8 +55,8 @@ impl EarlyLintPass for UnusedRounding { cx, UNUSED_ROUNDING, expr.span, - &format!("used the `{method_name}` method with a whole number float"), - &format!("remove the `{method_name}` method call"), + format!("used the `{method_name}` method with a whole number float"), + format!("remove the `{method_name}` method call"), float, Applicability::MachineApplicable, ); diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs index f2eb774b5cbfe..2622abd59cbd6 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap.rs @@ -338,7 +338,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { UNNECESSARY_UNWRAP, expr.hir_id, expr.span, - &format!( + format!( "called `{}` on `{unwrappable_variable_name}` after checking its variant with `{}`", method_name.ident.name, unwrappable.check_name.ident.as_str(), @@ -373,7 +373,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { PANICKING_UNWRAP, expr.hir_id, expr.span, - &format!("this call to `{}()` will always panic", method_name.ident.name), + format!("this call to `{}()` will always panic", method_name.ident.name), |diag| { diag.span_label(unwrappable.check.span, "because of this check"); }, diff --git a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs index a615ef116910a..aca500590cef1 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs @@ -59,7 +59,7 @@ declare_lint_pass!(UnwrapInResult=> [UNWRAP_IN_RESULT]); impl<'tcx> LateLintPass<'tcx> for UnwrapInResult { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { - if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind + if let ImplItemKind::Fn(ref _signature, _) = impl_item.kind // first check if it's a method or function // checking if its return type is `result` or `option` && (is_type_diagnostic_item(cx, return_ty(cx, impl_item.owner_id), sym::Result) diff --git a/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs b/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs index d2a1d42f27968..f376d3496461f 100644 --- a/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs +++ b/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs @@ -94,7 +94,7 @@ fn check_ident(cx: &LateContext<'_>, ident: &Ident, hir_id: HirId, be_aggressive UPPER_CASE_ACRONYMS, hir_id, span, - &format!("name `{ident}` contains a capitalized acronym"), + format!("name `{ident}` contains a capitalized acronym"), |diag| { diag.span_suggestion( span, diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs index a6b411d6c0f97..0bab917607daf 100644 --- a/src/tools/clippy/clippy_lints/src/use_self.rs +++ b/src/tools/clippy/clippy_lints/src/use_self.rs @@ -207,7 +207,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { } } - fn check_ty(&mut self, cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>) { + fn check_ty(&mut self, cx: &LateContext<'tcx>, hir_ty: &Ty<'tcx>) { if !hir_ty.span.from_expansion() && self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS) && let Some(&StackItem::Check { @@ -286,7 +286,7 @@ impl<'tcx> Visitor<'tcx> for SkipTyCollector { walk_inf(self, inf); } - fn visit_ty(&mut self, hir_ty: &hir::Ty<'_>) { + fn visit_ty(&mut self, hir_ty: &Ty<'_>) { self.types_to_skip.push(hir_ty.hir_id); walk_ty(self, hir_ty); diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs index f7a455977facd..7554176615655 100644 --- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -184,7 +184,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - &format!("useless conversion to the same type: `{b}`"), + format!("useless conversion to the same type: `{b}`"), "consider removing `.into()`", sugg.into_owned(), app, @@ -301,7 +301,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - &format!("useless conversion to the same type: `{b}`"), + format!("useless conversion to the same type: `{b}`"), "consider removing `.into_iter()`", sugg, Applicability::MachineApplicable, // snippet @@ -321,7 +321,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - &format!("useless conversion to the same type: `{b}`"), + format!("useless conversion to the same type: `{b}`"), None, "consider removing `.try_into()`", ); @@ -346,9 +346,9 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - &format!("useless conversion to the same type: `{b}`"), + format!("useless conversion to the same type: `{b}`"), None, - &hint, + hint, ); } @@ -360,8 +360,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - &format!("useless conversion to the same type: `{b}`"), - &sugg_msg, + format!("useless conversion to the same type: `{b}`"), + sugg_msg, sugg.to_string(), app, ); diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 5319915b2eac1..7b43abeef671c 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -649,6 +649,8 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { BindingAnnotation::REF => "REF", BindingAnnotation::MUT => "MUT", BindingAnnotation::REF_MUT => "REF_MUT", + BindingAnnotation::MUT_REF => "MUT_REF", + BindingAnnotation::MUT_REF_MUT => "MUT_REF_MUT", }; kind!("Binding(BindingAnnotation::{ann}, _, {name}, {sub})"); self.ident(name); @@ -752,7 +754,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } } -fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool { +fn has_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { let attrs = cx.tcx.hir().attrs(hir_id); get_attr(cx.sess(), attrs, "author").count() > 0 } @@ -769,7 +771,7 @@ fn path_to_string(path: &QPath<'_>) -> Result { } }, QPath::TypeRelative(ty, segment) => match &ty.kind { - hir::TyKind::Path(inner_path) => { + TyKind::Path(inner_path) => { inner(s, inner_path)?; *s += ", "; write!(s, "{:?}", segment.ident.as_str()).unwrap(); diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs index 4822970e47ef5..5483e80f932e1 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs @@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for AlmostStandardFormulation { ident.span, "non-standard lint formulation", None, - &format!("consider using `{}`", formulation.correction), + format!("consider using `{}`", formulation.correction), ); } return; diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs index 7c70d3f45dba3..f752968267553 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::{is_expr_path_def_path, is_lint_allowed, peel_blocks_with_stmt, SpanlessEq}; use rustc_errors::Applicability; -use rustc_hir as hir; use rustc_hir::{Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -72,7 +71,7 @@ declare_clippy_lint! { declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]); impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if is_lint_allowed(cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.hir_id) { return; } diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs index df37619227c86..9b6b687181867 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs @@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions { path.ident.span, "usage of a compiler lint function", None, - &format!("please use the Clippy variant of this function: `{sugg}`"), + format!("please use the Clippy variant of this function: `{sugg}`"), ); } } diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs index 370ed430bcfb5..9be225759df95 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -181,7 +181,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { cx, DEFAULT_LINT, item.span, - &format!("the lint `{}` has the default lint description", item.ident.name), + format!("the lint `{}` has the default lint description", item.ident.name), ); } @@ -191,7 +191,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { cx, DEFAULT_DEPRECATION_REASON, item.span, - &format!("the lint `{}` has the default deprecation reason", item.ident.name), + format!("the lint `{}` has the default deprecation reason", item.ident.name), ); } } @@ -247,7 +247,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { cx, LINT_WITHOUT_LINT_PASS, lint_span, - &format!("the lint `{lint_name}` is not added to any `LintPass`"), + format!("the lint `{lint_name}` is not added to any `LintPass`"), ); } } diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs index c56c8ddc7a928..5c1ebb922f125 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -153,7 +153,7 @@ impl MetadataCollector { lints: BinaryHeap::::default(), applicability_info: FxHashMap::::default(), config: get_configuration_metadata(), - clippy_project_root: std::env::current_dir() + clippy_project_root: env::current_dir() .expect("failed to get current dir") .ancestors() .nth(1) @@ -243,7 +243,7 @@ Please use that command to update the file and do not edit it by hand. .unwrap(); // Write configuration links to CHANGELOG.md - let changelog = std::fs::read_to_string(CHANGELOG_PATH).unwrap(); + let changelog = fs::read_to_string(CHANGELOG_PATH).unwrap(); let mut changelog_file = File::create(CHANGELOG_PATH).unwrap(); let position = changelog .find("") @@ -822,7 +822,7 @@ fn lint_collection_error_item(cx: &LateContext<'_>, item: &Item<'_>, message: &s cx, METADATA_COLLECTOR, item.ident.span, - &format!("metadata collection error for `{}`: {message}", item.ident.name), + format!("metadata collection error for `{}`: {message}", item.ident.name), ); } @@ -912,7 +912,7 @@ impl<'a, 'hir> LintResolver<'a, 'hir> { } } -impl<'a, 'hir> intravisit::Visitor<'hir> for LintResolver<'a, 'hir> { +impl<'a, 'hir> Visitor<'hir> for LintResolver<'a, 'hir> { type NestedFilter = nested_filter::All; fn nested_visit_map(&mut self) -> Self::Map { @@ -963,7 +963,7 @@ impl<'a, 'hir> ApplicabilityResolver<'a, 'hir> { } } -impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> { +impl<'a, 'hir> Visitor<'hir> for ApplicabilityResolver<'a, 'hir> { type NestedFilter = nested_filter::All; fn nested_visit_map(&mut self) -> Self::Map { @@ -994,7 +994,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> { } /// This returns the parent local node if the expression is a reference one -fn get_parent_local<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::Local<'hir>> { +fn get_parent_local<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::LetStmt<'hir>> { if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind { if let hir::def::Res::Local(local_hir) = path.res { return get_parent_local_hir_id(cx, local_hir); @@ -1004,7 +1004,7 @@ fn get_parent_local<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) - None } -fn get_parent_local_hir_id<'hir>(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> { +fn get_parent_local_hir_id<'hir>(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::LetStmt<'hir>> { match cx.tcx.parent_hir_node(hir_id) { hir::Node::LetStmt(local) => Some(local), hir::Node::Pat(pattern) => get_parent_local_hir_id(cx, pattern.hir_id), @@ -1042,7 +1042,7 @@ impl<'a, 'hir> IsMultiSpanScanner<'a, 'hir> { } } -impl<'a, 'hir> intravisit::Visitor<'hir> for IsMultiSpanScanner<'a, 'hir> { +impl<'a, 'hir> Visitor<'hir> for IsMultiSpanScanner<'a, 'hir> { type NestedFilter = nested_filter::All; fn nested_visit_map(&mut self) -> Self::Map { diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs index 8d208fbb7e954..63fcbd61528d2 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs @@ -51,8 +51,8 @@ impl LateLintPass<'_> for MsrvAttrImpl { cx, MISSING_MSRV_ATTR_IMPL, span, - &format!("`extract_msrv_attr!` macro missing from `{lint_pass}` implementation"), - &format!("add `extract_msrv_attr!({context})` to the `{lint_pass}` implementation"), + format!("`extract_msrv_attr!` macro missing from `{lint_pass}` implementation"), + format!("add `extract_msrv_attr!({context})` to the `{lint_pass}` implementation"), format!("{}\n extract_msrv_attr!({context});", snippet(cx, span, "..")), Applicability::MachineApplicable, ); diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs index 304a137937402..8cf42832761f5 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -4,10 +4,9 @@ use clippy_utils::{def_path_def_ids, is_lint_allowed, match_any_def_paths, peel_ use rustc_ast::ast::LitKind; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_errors::Applicability; -use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{Expr, ExprKind, Local, Mutability, Node}; +use rustc_hir::{Expr, ExprKind, LetStmt, Mutability, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::interpret::{Allocation, GlobalAlloc}; use rustc_middle::mir::ConstValue; @@ -49,7 +48,7 @@ pub struct UnnecessaryDefPath { } impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if is_lint_allowed(cx, UNNECESSARY_DEF_PATH, expr.hir_id) { return; } @@ -79,9 +78,9 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { cx, UNNECESSARY_DEF_PATH, span, - &format!("hardcoded path to a {msg}"), + format!("hardcoded path to a {msg}"), None, - &format!("convert all references to use `{sugg}`"), + format!("convert all references to use `{sugg}`"), ); } } @@ -213,11 +212,11 @@ impl UnnecessaryDefPath { } } -fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option> { +fn path_to_matched_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option> { match peel_hir_expr_refs(expr).0.kind { ExprKind::Path(ref qpath) => match cx.qpath_res(qpath, expr.hir_id) { Res::Local(hir_id) => { - if let Node::LetStmt(Local { init: Some(init), .. }) = cx.tcx.parent_hir_node(hir_id) { + if let Node::LetStmt(LetStmt { init: Some(init), .. }) = cx.tcx.parent_hir_node(hir_id) { path_to_matched_type(cx, init) } else { None diff --git a/src/tools/clippy/clippy_lints/src/visibility.rs b/src/tools/clippy/clippy_lints/src/visibility.rs index 83369c66367e7..9818b98dd5b40 100644 --- a/src/tools/clippy/clippy_lints/src/visibility.rs +++ b/src/tools/clippy/clippy_lints/src/visibility.rs @@ -89,7 +89,7 @@ impl EarlyLintPass for Visibility { cx, NEEDLESS_PUB_SELF, item.vis.span, - &format!("unnecessary `pub({}self)`", if *shorthand { "" } else { "in " }), + format!("unnecessary `pub({}self)`", if *shorthand { "" } else { "in " }), "remove it", String::new(), Applicability::MachineApplicable, diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index be16d2e5cc301..26c6859233d53 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -297,11 +297,11 @@ impl<'tcx> LateLintPass<'tcx> for Write { match diag_name { sym::print_macro | sym::println_macro if !allowed_in_tests => { if !is_build_script { - span_lint(cx, PRINT_STDOUT, macro_call.span, &format!("use of `{name}!`")); + span_lint(cx, PRINT_STDOUT, macro_call.span, format!("use of `{name}!`")); } }, sym::eprint_macro | sym::eprintln_macro if !allowed_in_tests => { - span_lint(cx, PRINT_STDERR, macro_call.span, &format!("use of `{name}!`")); + span_lint(cx, PRINT_STDERR, macro_call.span, format!("use of `{name}!`")); }, sym::write_macro | sym::writeln_macro => {}, _ => return, @@ -390,7 +390,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &Ma cx, lint, macro_call.span, - &format!("using `{name}!()` with a format string that ends in a single newline"), + format!("using `{name}!()` with a format string that ends in a single newline"), |diag| { let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!'); let Some(format_snippet) = snippet_opt(cx, format_string_span) else { @@ -440,7 +440,7 @@ fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call cx, lint, macro_call.span, - &format!("empty string literal in `{name}!`"), + format!("empty string literal in `{name}!`"), |diag| { diag.span_suggestion( span, diff --git a/src/tools/clippy/clippy_lints/src/zero_div_zero.rs b/src/tools/clippy/clippy_lints/src/zero_div_zero.rs index d3623d6fda442..662242f6196bc 100644 --- a/src/tools/clippy/clippy_lints/src/zero_div_zero.rs +++ b/src/tools/clippy/clippy_lints/src/zero_div_zero.rs @@ -53,7 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for ZeroDiv { expr.span, "constant division of `0.0` with `0.0` will always result in NaN", None, - &format!("consider using `{float_type}::NAN` if you would like a constant representing NaN",), + format!("consider using `{float_type}::NAN` if you would like a constant representing NaN",), ); } } diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index f7532121aeb5d..f594a40ff59ad 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -97,8 +97,8 @@ pub fn eq_path_seg(l: &PathSegment, r: &PathSegment) -> bool { pub fn eq_generic_args(l: &GenericArgs, r: &GenericArgs) -> bool { match (l, r) { - (GenericArgs::AngleBracketed(l), GenericArgs::AngleBracketed(r)) => over(&l.args, &r.args, eq_angle_arg), - (GenericArgs::Parenthesized(l), GenericArgs::Parenthesized(r)) => { + (AngleBracketed(l), AngleBracketed(r)) => over(&l.args, &r.args, eq_angle_arg), + (Parenthesized(l), Parenthesized(r)) => { over(&l.inputs, &r.inputs, |l, r| eq_ty(l, r)) && eq_fn_ret_ty(&l.output, &r.output) }, _ => false, @@ -304,25 +304,25 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { (ExternCrate(l), ExternCrate(r)) => l == r, (Use(l), Use(r)) => eq_use_tree(l, r), ( - Static(box ast::StaticItem { + Static(box StaticItem { ty: lt, mutability: lm, expr: le, }), - Static(box ast::StaticItem { + Static(box StaticItem { ty: rt, mutability: rm, expr: re, }), ) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re), ( - Const(box ast::ConstItem { + Const(box ConstItem { defaultness: ld, generics: lg, ty: lt, expr: le, }), - Const(box ast::ConstItem { + Const(box ConstItem { defaultness: rd, generics: rg, ty: rt, @@ -493,13 +493,13 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { use AssocItemKind::*; match (l, r) { ( - Const(box ast::ConstItem { + Const(box ConstItem { defaultness: ld, generics: lg, ty: lt, expr: le, }), - Const(box ast::ConstItem { + Const(box ConstItem { defaultness: rd, generics: rg, ty: rt, @@ -523,14 +523,14 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r)) }, ( - Type(box ast::TyAlias { + Type(box TyAlias { defaultness: ld, generics: lg, bounds: lb, ty: lt, .. }), - Type(box ast::TyAlias { + Type(box TyAlias { defaultness: rd, generics: rg, bounds: rb, diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs index 2d0c2cf125364..d2200bcf7103b 100644 --- a/src/tools/clippy/clippy_utils/src/attrs.rs +++ b/src/tools/clippy/clippy_utils/src/attrs.rs @@ -1,10 +1,15 @@ use rustc_ast::{ast, attr}; use rustc_errors::Applicability; +use rustc_lexer::TokenKind; +use rustc_lint::LateContext; use rustc_middle::ty::{AdtDef, TyCtxt}; use rustc_session::Session; -use rustc_span::sym; +use rustc_span::{sym, Span}; use std::str::FromStr; +use crate::source::snippet_opt; +use crate::tokenize_with_text; + /// Deprecation status of attributes known by Clippy. pub enum DeprecationStatus { /// Attribute is deprecated @@ -171,3 +176,28 @@ pub fn has_non_exhaustive_attr(tcx: TyCtxt<'_>, adt: AdtDef<'_>) -> bool { .all_fields() .any(|field_def| tcx.has_attr(field_def.did, sym::non_exhaustive)) } + +/// Checks if the given span contains a `#[cfg(..)]` attribute +pub fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool { + let Some(snip) = snippet_opt(cx, s) else { + // Assume true. This would require either an invalid span, or one which crosses file boundaries. + return true; + }; + let mut iter = tokenize_with_text(&snip); + + // Search for the token sequence [`#`, `[`, `cfg`] + while iter.any(|(t, _)| matches!(t, TokenKind::Pound)) { + let mut iter = iter.by_ref().skip_while(|(t, _)| { + matches!( + t, + TokenKind::Whitespace | TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } + ) + }); + if matches!(iter.next(), Some((TokenKind::OpenBracket, _))) + && matches!(iter.next(), Some((TokenKind::Ident, "cfg"))) + { + return true; + } + } + false +} diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs index d751aeaf90222..422673105136d 100644 --- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs +++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs @@ -24,7 +24,7 @@ use rustc_hir::{ use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; -use rustc_span::symbol::Ident; +use rustc_span::symbol::{kw, Ident}; use rustc_span::{Span, Symbol}; use rustc_target::spec::abi::Abi; @@ -99,9 +99,13 @@ fn qpath_search_pat(path: &QPath<'_>) -> (Pat, Pat) { let start = if ty.is_some() { Pat::Str("<") } else { - path.segments - .first() - .map_or(Pat::Str(""), |seg| Pat::Sym(seg.ident.name)) + path.segments.first().map_or(Pat::Str(""), |seg| { + if seg.ident.name == kw::PathRoot { + Pat::Str("::") + } else { + Pat::Sym(seg.ident.name) + } + }) }; let end = path.segments.last().map_or(Pat::Str(""), |seg| { if seg.args.is_some() { diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index 6b86630339fb5..253ae3aca6894 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -17,14 +17,14 @@ use rustc_span::def_id::DefId; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::SyntaxContext; use rustc_target::abi::Size; -use std::cmp::Ordering::{self, Equal}; +use std::cmp::Ordering; use std::hash::{Hash, Hasher}; use std::iter; /// A `LitKind`-like enum to fold constant `Expr`s into. #[derive(Debug, Clone)] pub enum Constant<'tcx> { - Adt(rustc_middle::mir::Const<'tcx>), + Adt(mir::Const<'tcx>), /// A `String` (e.g., "abc"). Str(String), /// A binary string (e.g., `b"abc"`). @@ -230,7 +230,7 @@ impl<'tcx> Constant<'tcx> { lv, rv, ) { - Some(Equal) => Some(ls.cmp(rs)), + Some(Ordering::Equal) => Some(ls.cmp(rs)), x => x, } }, @@ -579,7 +579,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { /// Lookup a possibly constant expression from an `ExprKind::Path` and apply a function on it. fn fetch_path_and_apply(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>, f: F) -> Option where - F: FnOnce(&mut Self, rustc_middle::mir::Const<'tcx>) -> Option, + F: FnOnce(&mut Self, mir::Const<'tcx>) -> Option, { let res = self.typeck_results.qpath_res(qpath, id); match res { @@ -612,7 +612,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { .tcx .const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, args), qpath.span()) .ok() - .map(|val| rustc_middle::mir::Const::from_value(val, ty))?; + .map(|val| mir::Const::from_value(val, ty))?; f(self, result) }, _ => None, diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs index 0352696f93eca..dc0a139e3c786 100644 --- a/src/tools/clippy/clippy_utils/src/diagnostics.rs +++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs @@ -8,7 +8,7 @@ //! Thank you! //! ~The `INTERNAL_METADATA_COLLECTOR` lint -use rustc_errors::{Applicability, Diag, MultiSpan}; +use rustc_errors::{Applicability, Diag, DiagMessage, MultiSpan, SubdiagMessage}; use rustc_hir::HirId; use rustc_lint::{LateContext, Lint, LintContext}; use rustc_span::Span; @@ -59,9 +59,9 @@ fn docs_link(diag: &mut Diag<'_, ()>, lint: &'static Lint) { /// 17 | std::mem::forget(seven); /// | ^^^^^^^^^^^^^^^^^^^^^^^ /// ``` -pub fn span_lint(cx: &T, lint: &'static Lint, sp: impl Into, msg: &str) { +pub fn span_lint(cx: &T, lint: &'static Lint, sp: impl Into, msg: impl Into) { #[expect(clippy::disallowed_methods)] - cx.span_lint(lint, sp, msg.to_string(), |diag| { + cx.span_lint(lint, sp, msg.into(), |diag| { docs_link(diag, lint); }); } @@ -104,17 +104,16 @@ pub fn span_lint_and_help( cx: &T, lint: &'static Lint, span: impl Into, - msg: &str, + msg: impl Into, help_span: Option, - help: &str, + help: impl Into, ) { #[expect(clippy::disallowed_methods)] - cx.span_lint(lint, span, msg.to_string(), |diag| { - let help = help.to_string(); + cx.span_lint(lint, span, msg.into(), |diag| { if let Some(help_span) = help_span { - diag.span_help(help_span, help); + diag.span_help(help_span, help.into()); } else { - diag.help(help); + diag.help(help.into()); } docs_link(diag, lint); }); @@ -161,17 +160,16 @@ pub fn span_lint_and_note( cx: &T, lint: &'static Lint, span: impl Into, - msg: &str, + msg: impl Into, note_span: Option, - note: &str, + note: impl Into, ) { #[expect(clippy::disallowed_methods)] - cx.span_lint(lint, span, msg.to_string(), |diag| { - let note = note.to_string(); + cx.span_lint(lint, span, msg.into(), |diag| { if let Some(note_span) = note_span { - diag.span_note(note_span, note); + diag.span_note(note_span, note.into()); } else { - diag.note(note); + diag.note(note.into()); } docs_link(diag, lint); }); @@ -195,14 +193,15 @@ pub fn span_lint_and_note( /// If you're unsure which function you should use, you can test if the `#[allow]` attribute works /// where you would expect it to. /// If it doesn't, you likely need to use [`span_lint_hir_and_then`] instead. -pub fn span_lint_and_then(cx: &C, lint: &'static Lint, sp: S, msg: &str, f: F) +pub fn span_lint_and_then(cx: &C, lint: &'static Lint, sp: S, msg: M, f: F) where C: LintContext, S: Into, + M: Into, F: FnOnce(&mut Diag<'_, ()>), { #[expect(clippy::disallowed_methods)] - cx.span_lint(lint, sp, msg.to_string(), |diag| { + cx.span_lint(lint, sp, msg, |diag| { f(diag); docs_link(diag, lint); }); @@ -232,9 +231,9 @@ where /// Instead, use this function and also pass the `HirId` of ``, which will let /// the compiler check lint level attributes at the place of the expression and /// the `#[allow]` will work. -pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: &str) { +pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: impl Into) { #[expect(clippy::disallowed_methods)] - cx.tcx.node_span_lint(lint, hir_id, sp, msg.to_string(), |diag| { + cx.tcx.node_span_lint(lint, hir_id, sp, msg.into(), |diag| { docs_link(diag, lint); }); } @@ -268,11 +267,11 @@ pub fn span_lint_hir_and_then( lint: &'static Lint, hir_id: HirId, sp: impl Into, - msg: &str, + msg: impl Into, f: impl FnOnce(&mut Diag<'_, ()>), ) { #[expect(clippy::disallowed_methods)] - cx.tcx.node_span_lint(lint, hir_id, sp, msg.to_string(), |diag| { + cx.tcx.node_span_lint(lint, hir_id, sp, msg.into(), |diag| { f(diag); docs_link(diag, lint); }); @@ -316,13 +315,13 @@ pub fn span_lint_and_sugg( cx: &T, lint: &'static Lint, sp: Span, - msg: &str, - help: &str, + msg: impl Into, + help: impl Into, sugg: String, applicability: Applicability, ) { - span_lint_and_then(cx, lint, sp, msg, |diag| { - diag.span_suggestion(sp, help.to_string(), sugg, applicability); + span_lint_and_then(cx, lint, sp, msg.into(), |diag| { + diag.span_suggestion(sp, help.into(), sugg, applicability); }); } @@ -332,7 +331,7 @@ pub fn span_lint_and_sugg( /// appear once per /// replacement. In human-readable format though, it only appears once before /// the whole suggestion. -pub fn multispan_sugg(diag: &mut Diag<'_, ()>, help_msg: &str, sugg: I) +pub fn multispan_sugg(diag: &mut Diag<'_, ()>, help_msg: impl Into, sugg: I) where I: IntoIterator, { @@ -346,11 +345,11 @@ where /// Suggestions with multiple spans will be silently ignored. pub fn multispan_sugg_with_applicability( diag: &mut Diag<'_, ()>, - help_msg: &str, + help_msg: impl Into, applicability: Applicability, sugg: I, ) where I: IntoIterator, { - diag.multipart_suggestion(help_msg.to_string(), sugg.into_iter().collect(), applicability); + diag.multipart_suggestion(help_msg.into(), sugg.into_iter().collect(), applicability); } diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index 8ce19998a0828..801a98521590d 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -16,11 +16,11 @@ use rustc_span::{sym, symbol, Span}; /// `for pat in arg { body }` becomes `(pat, arg, body)`. Returns `(pat, arg, body, span)`. pub struct ForLoop<'tcx> { /// `for` loop item - pub pat: &'tcx hir::Pat<'tcx>, + pub pat: &'tcx Pat<'tcx>, /// `IntoIterator` argument - pub arg: &'tcx hir::Expr<'tcx>, + pub arg: &'tcx Expr<'tcx>, /// `for` loop body - pub body: &'tcx hir::Expr<'tcx>, + pub body: &'tcx Expr<'tcx>, /// Compare this against `hir::Destination.target` pub loop_id: HirId, /// entire `for` loop span @@ -30,13 +30,13 @@ pub struct ForLoop<'tcx> { impl<'tcx> ForLoop<'tcx> { /// Parses a desugared `for` loop pub fn hir(expr: &Expr<'tcx>) -> Option { - if let hir::ExprKind::DropTemps(e) = expr.kind - && let hir::ExprKind::Match(iterexpr, [arm], hir::MatchSource::ForLoopDesugar) = e.kind - && let hir::ExprKind::Call(_, [arg]) = iterexpr.kind - && let hir::ExprKind::Loop(block, ..) = arm.body.kind + if let ExprKind::DropTemps(e) = expr.kind + && let ExprKind::Match(iterexpr, [arm], MatchSource::ForLoopDesugar) = e.kind + && let ExprKind::Call(_, [arg]) = iterexpr.kind + && let ExprKind::Loop(block, ..) = arm.body.kind && let [stmt] = block.stmts && let hir::StmtKind::Expr(e) = stmt.kind - && let hir::ExprKind::Match(_, [_, some_arm], _) = e.kind + && let ExprKind::Match(_, [_, some_arm], _) = e.kind && let hir::PatKind::Struct(_, [field], _) = some_arm.pat.kind { return Some(Self { @@ -209,28 +209,28 @@ impl<'hir> IfOrIfLet<'hir> { #[derive(Debug, Copy, Clone)] pub struct Range<'a> { /// The lower bound of the range, or `None` for ranges such as `..X`. - pub start: Option<&'a hir::Expr<'a>>, + pub start: Option<&'a Expr<'a>>, /// The upper bound of the range, or `None` for ranges such as `X..`. - pub end: Option<&'a hir::Expr<'a>>, + pub end: Option<&'a Expr<'a>>, /// Whether the interval is open or closed. pub limits: ast::RangeLimits, } impl<'a> Range<'a> { /// Higher a `hir` range to something similar to `ast::ExprKind::Range`. - pub fn hir(expr: &'a hir::Expr<'_>) -> Option> { + pub fn hir(expr: &'a Expr<'_>) -> Option> { /// Finds the field named `name` in the field. Always return `Some` for /// convenience. - fn get_field<'c>(name: &str, fields: &'c [hir::ExprField<'_>]) -> Option<&'c hir::Expr<'c>> { + fn get_field<'c>(name: &str, fields: &'c [hir::ExprField<'_>]) -> Option<&'c Expr<'c>> { let expr = &fields.iter().find(|field| field.ident.name.as_str() == name)?.expr; Some(expr) } match expr.kind { - hir::ExprKind::Call(path, args) + ExprKind::Call(path, args) if matches!( path.kind, - hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, ..)) + ExprKind::Path(QPath::LangItem(hir::LangItem::RangeInclusiveNew, ..)) ) => { Some(Range { @@ -239,28 +239,28 @@ impl<'a> Range<'a> { limits: ast::RangeLimits::Closed, }) }, - hir::ExprKind::Struct(path, fields, None) => match &path { - hir::QPath::LangItem(hir::LangItem::RangeFull, ..) => Some(Range { + ExprKind::Struct(path, fields, None) => match &path { + QPath::LangItem(hir::LangItem::RangeFull, ..) => Some(Range { start: None, end: None, limits: ast::RangeLimits::HalfOpen, }), - hir::QPath::LangItem(hir::LangItem::RangeFrom, ..) => Some(Range { + QPath::LangItem(hir::LangItem::RangeFrom, ..) => Some(Range { start: Some(get_field("start", fields)?), end: None, limits: ast::RangeLimits::HalfOpen, }), - hir::QPath::LangItem(hir::LangItem::Range, ..) => Some(Range { + QPath::LangItem(hir::LangItem::Range, ..) => Some(Range { start: Some(get_field("start", fields)?), end: Some(get_field("end", fields)?), limits: ast::RangeLimits::HalfOpen, }), - hir::QPath::LangItem(hir::LangItem::RangeToInclusive, ..) => Some(Range { + QPath::LangItem(hir::LangItem::RangeToInclusive, ..) => Some(Range { start: None, end: Some(get_field("end", fields)?), limits: ast::RangeLimits::Closed, }), - hir::QPath::LangItem(hir::LangItem::RangeTo, ..) => Some(Range { + QPath::LangItem(hir::LangItem::RangeTo, ..) => Some(Range { start: None, end: Some(get_field("end", fields)?), limits: ast::RangeLimits::HalfOpen, @@ -275,17 +275,17 @@ impl<'a> Range<'a> { /// Represents the pre-expansion arguments of a `vec!` invocation. pub enum VecArgs<'a> { /// `vec![elem; len]` - Repeat(&'a hir::Expr<'a>, &'a hir::Expr<'a>), + Repeat(&'a Expr<'a>, &'a Expr<'a>), /// `vec![a, b, c]` - Vec(&'a [hir::Expr<'a>]), + Vec(&'a [Expr<'a>]), } impl<'a> VecArgs<'a> { /// Returns the arguments of the `vec!` macro if this expression was expanded /// from `vec!`. - pub fn hir(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> Option> { - if let hir::ExprKind::Call(fun, args) = expr.kind - && let hir::ExprKind::Path(ref qpath) = fun.kind + pub fn hir(cx: &LateContext<'_>, expr: &'a Expr<'_>) -> Option> { + if let ExprKind::Call(fun, args) = expr.kind + && let ExprKind::Path(ref qpath) = fun.kind && is_expn_of(fun.span, "vec").is_some() && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id() { @@ -294,8 +294,8 @@ impl<'a> VecArgs<'a> { Some(VecArgs::Repeat(&args[0], &args[1])) } else if match_def_path(cx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 { // `vec![a, b, c]` case - if let hir::ExprKind::Call(_, [arg]) = &args[0].kind - && let hir::ExprKind::Array(args) = arg.kind + if let ExprKind::Call(_, [arg]) = &args[0].kind + && let ExprKind::Array(args) = arg.kind { Some(VecArgs::Vec(args)) } else { diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 7a4eba9790ed4..f8bbe99777483 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -954,8 +954,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_pat(pat); } }, - PatKind::Box(pat) => self.hash_pat(pat), - PatKind::Deref(pat) => self.hash_pat(pat), + PatKind::Box(pat) | PatKind::Deref(pat) => self.hash_pat(pat), PatKind::Lit(expr) => self.hash_expr(expr), PatKind::Or(pats) => { for pat in pats { diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 8251bdf78fc7d..37c12dd850c27 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -16,12 +16,14 @@ rustc::diagnostic_outside_of_impl, rustc::untranslatable_diagnostic )] -// warn on the same lints as `clippy_lints` -#![warn(trivial_casts, trivial_numeric_casts)] -// warn on lints, that are included in `rust-lang/rust`s bootstrap -#![warn(rust_2018_idioms, unused_lifetimes)] -// warn on rustc internal lints -#![warn(rustc::internal)] +#![warn( + trivial_casts, + trivial_numeric_casts, + rust_2018_idioms, + unused_lifetimes, + unused_qualifications, + rustc::internal +)] // FIXME: switch to something more ergonomic here, once available. // (Currently there is no way to opt into sysroot crates without `extern crate`.) @@ -97,17 +99,16 @@ use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::{ - self as hir, def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Destination, Expr, + self as hir, def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, ByRef, Closure, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, Item, - ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment, PrimTy, - QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, + ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment, + PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, }; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::place::PlaceBase; use rustc_middle::mir::Const; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; -use rustc_middle::ty::binding::BindingMode; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ @@ -298,9 +299,10 @@ pub fn is_ty_alias(qpath: &QPath<'_>) -> bool { /// Checks if the method call given in `expr` belongs to the given trait. /// This is a deprecated function, consider using [`is_trait_method`]. pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool { - let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); - let trt_id = cx.tcx.trait_of_item(def_id); - trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path)) + cx.typeck_results() + .type_dependent_def_id(expr.hir_id) + .and_then(|defid| cx.tcx.trait_of_item(defid)) + .map_or(false, |trt_id| match_def_path(cx, trt_id, path)) } /// Checks if a method is defined in an impl of a diagnostic item @@ -349,7 +351,7 @@ pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool /// refers to an item of the trait `Default`, which is associated with the /// `diag_item` of `sym::Default`. pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool { - if let hir::ExprKind::Path(ref qpath) = expr.kind { + if let ExprKind::Path(ref qpath) = expr.kind { cx.qpath_res(qpath, expr.hir_id) .opt_def_id() .map_or(false, |def_id| is_diag_trait_item(cx, def_id, diag_item)) @@ -723,8 +725,8 @@ pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, def_id: LocalDefId) -> let hir_id = cx.tcx.local_def_id_to_hir_id(def_id); let parent_impl = cx.tcx.hir().get_parent_item(hir_id); if parent_impl != hir::CRATE_OWNER_ID - && let hir::Node::Item(item) = cx.tcx.hir_node_by_def_id(parent_impl.def_id) - && let hir::ItemKind::Impl(impl_) = &item.kind + && let Node::Item(item) = cx.tcx.hir_node_by_def_id(parent_impl.def_id) + && let ItemKind::Impl(impl_) = &item.kind { return impl_.of_trait.as_ref(); } @@ -830,7 +832,7 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath< /// Returns true if the expr is equal to `Default::default` when evaluated. pub fn is_default_equivalent_call(cx: &LateContext<'_>, repl_func: &Expr<'_>) -> bool { - if let hir::ExprKind::Path(ref repl_func_qpath) = repl_func.kind + if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind && let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() && (is_diag_trait_item(cx, repl_def_id, sym::Default) || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath)) @@ -1006,11 +1008,12 @@ pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind { .typeck_results() .extract_binding_mode(cx.sess(), id, span) .unwrap() + .0 { - BindingMode::BindByValue(_) if !is_copy(cx, cx.typeck_results().node_type(id)) => { + ByRef::No if !is_copy(cx, cx.typeck_results().node_type(id)) => { capture = CaptureKind::Value; }, - BindingMode::BindByReference(Mutability::Mut) if capture != CaptureKind::Value => { + ByRef::Yes(Mutability::Mut) if capture != CaptureKind::Value => { capture = CaptureKind::Ref(Mutability::Mut); }, _ => (), @@ -1295,7 +1298,7 @@ pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext< /// Returns `true` if `expr` contains a return expression pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool { for_each_expr(expr, |e| { - if matches!(e.kind, hir::ExprKind::Ret(..)) { + if matches!(e.kind, ExprKind::Ret(..)) { ControlFlow::Break(()) } else { ControlFlow::Continue(()) @@ -1311,7 +1314,7 @@ pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'t /// This retrieves the parent for the given `HirId` if it's an expression. This is useful for /// constraint lints -pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: hir::HirId) -> Option<&'tcx Expr<'tcx>> { +pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> { match cx.tcx.parent_hir_node(hir_id) { Node::Expr(parent) => Some(parent), _ => None, @@ -1635,13 +1638,13 @@ pub fn is_direct_expn_of(span: Span, name: &str) -> Option { } /// Convenience function to get the return type of a function. -pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_def_id: hir::OwnerId) -> Ty<'tcx> { +pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId) -> Ty<'tcx> { let ret_ty = cx.tcx.fn_sig(fn_def_id).instantiate_identity().output(); cx.tcx.instantiate_bound_regions_with_erased(ret_ty) } /// Convenience function to get the nth argument type of a function. -pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: hir::OwnerId, nth: usize) -> Ty<'tcx> { +pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId, nth: usize) -> Ty<'tcx> { let arg = cx.tcx.fn_sig(fn_def_id).instantiate_identity().input(nth); cx.tcx.instantiate_bound_regions_with_erased(arg) } @@ -1652,8 +1655,8 @@ pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_ if let ExprKind::Path(ref qp) = fun.kind { let res = cx.qpath_res(qp, fun.hir_id); return match res { - def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true, - def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id), + Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true, + Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id), _ => false, }; } @@ -1667,7 +1670,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { fn is_enum_variant(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool { matches!( cx.qpath_res(qpath, id), - def::Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _) + Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _) ) } @@ -1823,26 +1826,26 @@ pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> { pat } -pub fn int_bits(tcx: TyCtxt<'_>, ity: rustc_ty::IntTy) -> u64 { +pub fn int_bits(tcx: TyCtxt<'_>, ity: IntTy) -> u64 { Integer::from_int_ty(&tcx, ity).size().bits() } #[expect(clippy::cast_possible_wrap)] /// Turn a constant int byte representation into an i128 -pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::IntTy) -> i128 { +pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: IntTy) -> i128 { let amt = 128 - int_bits(tcx, ity); ((u as i128) << amt) >> amt } #[expect(clippy::cast_sign_loss)] /// clip unused bytes -pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: rustc_ty::IntTy) -> u128 { +pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: IntTy) -> u128 { let amt = 128 - int_bits(tcx, ity); ((u as u128) << amt) >> amt } /// clip unused bytes -pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::UintTy) -> u128 { +pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: UintTy) -> u128 { let bits = Integer::from_uint_ty(&tcx, ity).size().bits(); let amt = 128 - bits; (u << amt) >> amt @@ -2007,7 +2010,7 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let did = match expr.kind { ExprKind::Call(path, _) => { if let ExprKind::Path(ref qpath) = path.kind - && let def::Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id) + && let Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id) { Some(did) } else { @@ -2035,7 +2038,7 @@ fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool { .typeck_results() .pat_binding_modes() .get(pat.hir_id) - .is_some_and(|mode| matches!(mode, BindingMode::BindByReference(_))) + .is_some_and(|mode| matches!(mode.0, ByRef::Yes(_))) { // If a tuple `(x, y)` is of type `&(i32, i32)`, then due to match ergonomics, // the inner patterns become references. Don't consider this the identity function @@ -2218,7 +2221,7 @@ pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool { /// ``` pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool { if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) { - matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })) + matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. })) } else { false } @@ -2254,7 +2257,7 @@ pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { pub fn fn_def_id_with_node_args<'tcx>( cx: &LateContext<'tcx>, expr: &Expr<'_>, -) -> Option<(DefId, rustc_ty::GenericArgsRef<'tcx>)> { +) -> Option<(DefId, GenericArgsRef<'tcx>)> { let typeck = cx.typeck_results(); match &expr.kind { ExprKind::MethodCall(..) => Some(( @@ -2500,7 +2503,7 @@ fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl Fn(&[Sym /// Checks if the function containing the given `HirId` is a `#[test]` function /// /// Note: Add `//@compile-flags: --test` to UI tests with a `#[test]` function -pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { +pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool { with_test_item_names(tcx, tcx.parent_module(id), |names| { tcx.hir() .parent_iter(id) @@ -2523,7 +2526,7 @@ pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { /// /// This only checks directly applied attributes, to see if a node is inside a `#[cfg(test)]` parent /// use [`is_in_cfg_test`] -pub fn is_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { +pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool { tcx.hir().attrs(id).iter().any(|attr| { if attr.has_name(sym::cfg) && let Some(items) = attr.meta_item_list() @@ -2538,7 +2541,7 @@ pub fn is_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { } /// Checks if any parent node of `HirId` has `#[cfg(test)]` attribute applied -pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { +pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool { tcx.hir() .parent_id_iter(id) .any(|parent_id| is_cfg_test(tcx, parent_id)) @@ -3333,3 +3336,12 @@ fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> St repeat(String::from("super")).take(go_up_by).chain(path).join("::") } } + +/// Returns true if the specified `HirId` is the top-level expression of a statement or the only +/// expression in a block. +pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool { + matches!( + cx.tcx.parent_hir_node(id), + Node::Stmt(..) | Node::Block(Block { stmts: &[], .. }) + ) +} diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs index c475e7b7c4356..f166087dc3caa 100644 --- a/src/tools/clippy/clippy_utils/src/macros.rs +++ b/src/tools/clippy/clippy_utils/src/macros.rs @@ -429,7 +429,7 @@ pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId) pub fn find_format_arg_expr<'hir, 'ast>( start: &'hir Expr<'hir>, target: &'ast FormatArgument, -) -> Result<&'hir rustc_hir::Expr<'hir>, &'ast rustc_ast::Expr> { +) -> Result<&'hir Expr<'hir>, &'ast rustc_ast::Expr> { let SpanData { lo, hi, diff --git a/src/tools/clippy/clippy_utils/src/mir/mod.rs b/src/tools/clippy/clippy_utils/src/mir/mod.rs index 9dbb4c68d13f8..e4966690d8c5c 100644 --- a/src/tools/clippy/clippy_utils/src/mir/mod.rs +++ b/src/tools/clippy/clippy_utils/src/mir/mod.rs @@ -111,7 +111,7 @@ pub fn block_in_cycle(body: &Body<'_>, block: BasicBlock) -> bool { } /// Convenience wrapper around `visit_local_usage`. -pub fn used_exactly_once(mir: &Body<'_>, local: rustc_middle::mir::Local) -> Option { +pub fn used_exactly_once(mir: &Body<'_>, local: Local) -> Option { visit_local_usage( &[local], mir, diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index cabebf89becc8..325c9bee05782 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -112,7 +112,7 @@ fn check_rvalue<'tcx>( Rvalue::Repeat(operand, _) | Rvalue::Use(operand) | Rvalue::Cast( - CastKind::PointerFromExposedAddress + CastKind::PointerWithExposedProvenance | CastKind::IntToInt | CastKind::FloatToInt | CastKind::IntToFloat @@ -149,7 +149,7 @@ fn check_rvalue<'tcx>( Err((span, "unsizing casts are not allowed in const fn".into())) } }, - Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => { + Rvalue::Cast(CastKind::PointerExposeProvenance, _, _) => { Err((span, "casting pointers to ints is unstable in const fn".into())) }, Rvalue::Cast(CastKind::DynStar, _, _) => { diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index 5090d0bd98b16..8d6057272c4e7 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -41,7 +41,7 @@ pub const ONE: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("1")); pub const EMPTY: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("")); impl Display for Sugg<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { match *self { Sugg::NonParen(ref s) | Sugg::MaybeParen(ref s) => s.fmt(f), Sugg::BinOp(op, ref lhs, ref rhs) => binop_to_string(op, lhs, rhs).fmt(f), @@ -124,48 +124,48 @@ impl<'a> Sugg<'a> { } match expr.kind { - hir::ExprKind::AddrOf(..) - | hir::ExprKind::If(..) - | hir::ExprKind::Let(..) - | hir::ExprKind::Closure { .. } - | hir::ExprKind::Unary(..) - | hir::ExprKind::Match(..) => Sugg::MaybeParen(get_snippet(expr.span)), - hir::ExprKind::Continue(..) - | hir::ExprKind::Yield(..) - | hir::ExprKind::Array(..) - | hir::ExprKind::Block(..) - | hir::ExprKind::Break(..) - | hir::ExprKind::Call(..) - | hir::ExprKind::Field(..) - | hir::ExprKind::Index(..) - | hir::ExprKind::InlineAsm(..) - | hir::ExprKind::OffsetOf(..) - | hir::ExprKind::ConstBlock(..) - | hir::ExprKind::Lit(..) - | hir::ExprKind::Loop(..) - | hir::ExprKind::MethodCall(..) - | hir::ExprKind::Path(..) - | hir::ExprKind::Repeat(..) - | hir::ExprKind::Ret(..) - | hir::ExprKind::Become(..) - | hir::ExprKind::Struct(..) - | hir::ExprKind::Tup(..) - | hir::ExprKind::Err(_) => Sugg::NonParen(get_snippet(expr.span)), - hir::ExprKind::DropTemps(inner) => Self::hir_from_snippet(inner, get_snippet), - hir::ExprKind::Assign(lhs, rhs, _) => { + ExprKind::AddrOf(..) + | ExprKind::If(..) + | ExprKind::Let(..) + | ExprKind::Closure { .. } + | ExprKind::Unary(..) + | ExprKind::Match(..) => Sugg::MaybeParen(get_snippet(expr.span)), + ExprKind::Continue(..) + | ExprKind::Yield(..) + | ExprKind::Array(..) + | ExprKind::Block(..) + | ExprKind::Break(..) + | ExprKind::Call(..) + | ExprKind::Field(..) + | ExprKind::Index(..) + | ExprKind::InlineAsm(..) + | ExprKind::OffsetOf(..) + | ExprKind::ConstBlock(..) + | ExprKind::Lit(..) + | ExprKind::Loop(..) + | ExprKind::MethodCall(..) + | ExprKind::Path(..) + | ExprKind::Repeat(..) + | ExprKind::Ret(..) + | ExprKind::Become(..) + | ExprKind::Struct(..) + | ExprKind::Tup(..) + | ExprKind::Err(_) => Sugg::NonParen(get_snippet(expr.span)), + ExprKind::DropTemps(inner) => Self::hir_from_snippet(inner, get_snippet), + ExprKind::Assign(lhs, rhs, _) => { Sugg::BinOp(AssocOp::Assign, get_snippet(lhs.span), get_snippet(rhs.span)) }, - hir::ExprKind::AssignOp(op, lhs, rhs) => { + ExprKind::AssignOp(op, lhs, rhs) => { Sugg::BinOp(hirbinop2assignop(op), get_snippet(lhs.span), get_snippet(rhs.span)) }, - hir::ExprKind::Binary(op, lhs, rhs) => Sugg::BinOp( + ExprKind::Binary(op, lhs, rhs) => Sugg::BinOp( AssocOp::from_ast_binop(op.node), get_snippet(lhs.span), get_snippet(rhs.span), ), - hir::ExprKind::Cast(lhs, ty) | + ExprKind::Cast(lhs, ty) | //FIXME(chenyukang), remove this after type ascription is removed from AST - hir::ExprKind::Type(lhs, ty) => Sugg::BinOp(AssocOp::As, get_snippet(lhs.span), get_snippet(ty.span)), + ExprKind::Type(lhs, ty) => Sugg::BinOp(AssocOp::As, get_snippet(lhs.span), get_snippet(ty.span)), } } @@ -358,6 +358,13 @@ impl<'a> Sugg<'a> { }, } } + + pub fn into_string(self) -> String { + match self { + Sugg::NonParen(p) | Sugg::MaybeParen(p) => p.into_owned(), + Sugg::BinOp(b, l, r) => binop_to_string(b, &l, &r), + } + } } /// Generates a string from the operator and both sides. @@ -508,7 +515,7 @@ impl ParenHelper { } impl Display for ParenHelper { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { if self.paren { write!(f, "({})", self.wrapped) } else { @@ -801,7 +808,7 @@ pub struct DerefClosure { /// /// note: this only works on single line immutable closures with exactly one input parameter. pub fn deref_closure_args(cx: &LateContext<'_>, closure: &hir::Expr<'_>) -> Option { - if let hir::ExprKind::Closure(&Closure { + if let ExprKind::Closure(&Closure { fn_decl, def_id, body, .. }) = closure.kind { diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index 97ce755adbbe6..ab1be66dc7878 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -91,12 +91,16 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<' return true; } - if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *inner_ty.kind() { + if let ty::Alias(ty::Opaque, AliasTy { def_id, .. }) = *inner_ty.kind() { if !seen.insert(def_id) { return false; } - for (predicate, _span) in cx.tcx.explicit_item_super_predicates(def_id).instantiate_identity_iter_copied() { + for (predicate, _span) in cx + .tcx + .explicit_item_super_predicates(def_id) + .instantiate_identity_iter_copied() + { match predicate.kind().skip_binder() { // For `impl Trait`, it will register a predicate of `T: Trait`, so we go through // and check substitutions to find `U`. @@ -159,6 +163,16 @@ pub fn get_type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option, ty: Ty<'_>) -> bool { + matches!( + get_type_diagnostic_name(cx, ty), + Some(sym::Arc | sym::ArcWeak | sym::Rc | sym::RcWeak) + ) +} + /// Returns true if ty has `iter` or `iter_mut` methods pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option { // FIXME: instead of this hard-coded list, we should check if `::iter` @@ -301,7 +315,7 @@ pub fn implements_trait_with_env_from_iter<'tcx>( cause: ObligationCause::dummy(), param_env, recursion_depth: 0, - predicate: ty::Binder::dummy(trait_ref).to_predicate(tcx), + predicate: Binder::dummy(trait_ref).to_predicate(tcx), }; infcx .evaluate_obligation(&obligation) @@ -327,7 +341,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { is_must_use_ty(cx, *ty) }, ty::Tuple(args) => args.iter().any(|ty| is_must_use_ty(cx, ty)), - ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => { + ty::Alias(ty::Opaque, AliasTy { def_id, .. }) => { for (predicate, _) in cx.tcx.explicit_item_super_predicates(def_id).skip_binder() { if let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() { if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) { @@ -356,13 +370,13 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { // not succeed /// Checks if `Ty` is normalizable. This function is useful /// to avoid crashes on `layout_of`. -pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { +pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { is_normalizable_helper(cx, param_env, ty, &mut FxHashMap::default()) } fn is_normalizable_helper<'tcx>( cx: &LateContext<'tcx>, - param_env: ty::ParamEnv<'tcx>, + param_env: ParamEnv<'tcx>, ty: Ty<'tcx>, cache: &mut FxHashMap, bool>, ) -> bool { @@ -372,7 +386,7 @@ fn is_normalizable_helper<'tcx>( // prevent recursive loops, false-negative is better than endless loop leading to stack overflow cache.insert(ty, false); let infcx = cx.tcx.infer_ctxt().build(); - let cause = rustc_middle::traits::ObligationCause::dummy(); + let cause = ObligationCause::dummy(); let result = if infcx.at(&cause, param_env).query_normalize(ty).is_ok() { match ty.kind() { ty::Adt(def, args) => def.variants().iter().all(|variant| { @@ -446,7 +460,7 @@ pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symb /// Checks if the type is equal to a lang item. /// /// Returns `false` if the `LangItem` is not defined. -pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool { +pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: LangItem) -> bool { match ty.kind() { ty::Adt(adt, _) => cx.tcx.lang_items().get(lang_item) == Some(adt.did()), _ => false, @@ -726,7 +740,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option Some(ExprFnSig::Sig(cx.tcx.fn_sig(id).instantiate(cx.tcx, subs), Some(id))), - ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => sig_from_bounds( + ty::Alias(ty::Opaque, AliasTy { def_id, args, .. }) => sig_from_bounds( cx, ty, cx.tcx.item_super_predicates(def_id).iter_instantiated(cx.tcx, args), @@ -807,7 +821,7 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: AliasTy<'tcx>) -> Option for (pred, _) in cx .tcx - .explicit_item_super_predicates(ty.def_id) + .explicit_item_bounds(ty.def_id) .iter_instantiated_copied(cx.tcx, ty.args) { match pred.kind().skip_binder() { @@ -899,7 +913,7 @@ pub fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { if let ty::Adt(adt, _) = ty.kind() && let &[krate, .., name] = &*cx.get_def_path(adt.did()) && let sym::libc | sym::core | sym::std = krate - && name == rustc_span::sym::c_void + && name == sym::c_void { true } else { @@ -1134,7 +1148,7 @@ pub fn make_projection<'tcx>( #[cfg(debug_assertions)] assert_generic_args_match(tcx, assoc_item.def_id, args); - Some(ty::AliasTy::new(tcx, assoc_item.def_id, args)) + Some(AliasTy::new(tcx, assoc_item.def_id, args)) } helper( tcx, @@ -1251,7 +1265,7 @@ pub fn make_normalized_projection_with_regions<'tcx>( ); return None; } - let cause = rustc_middle::traits::ObligationCause::dummy(); + let cause = ObligationCause::dummy(); match tcx .infer_ctxt() .build() @@ -1269,7 +1283,7 @@ pub fn make_normalized_projection_with_regions<'tcx>( } pub fn normalize_with_regions<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { - let cause = rustc_middle::traits::ObligationCause::dummy(); + let cause = ObligationCause::dummy(); match tcx.infer_ctxt().build().at(&cause, param_env).query_normalize(ty) { Ok(ty) => ty.value, Err(_) => ty, diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs index ec131c7f6a318..a145920aa85ed 100644 --- a/src/tools/clippy/clippy_utils/src/usage.rs +++ b/src/tools/clippy/clippy_utils/src/usage.rs @@ -83,15 +83,15 @@ impl<'tcx> Delegate<'tcx> for MutVarsDelegate { self.update(cmt); } - fn fake_read(&mut self, _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} + fn fake_read(&mut self, _: &PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } pub struct ParamBindingIdCollector { - pub binding_hir_ids: Vec, + pub binding_hir_ids: Vec, } impl<'tcx> ParamBindingIdCollector { - fn collect_binding_hir_ids(body: &'tcx hir::Body<'tcx>) -> Vec { - let mut hir_ids: Vec = Vec::new(); + fn collect_binding_hir_ids(body: &'tcx hir::Body<'tcx>) -> Vec { + let mut hir_ids: Vec = Vec::new(); for param in body.params { let mut finder = ParamBindingIdCollector { binding_hir_ids: Vec::new(), @@ -104,7 +104,7 @@ impl<'tcx> ParamBindingIdCollector { hir_ids } } -impl<'tcx> intravisit::Visitor<'tcx> for ParamBindingIdCollector { +impl<'tcx> Visitor<'tcx> for ParamBindingIdCollector { fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) { if let hir::PatKind::Binding(_, hir_id, ..) = pat.kind { self.binding_hir_ids.push(hir_id); @@ -115,7 +115,7 @@ impl<'tcx> intravisit::Visitor<'tcx> for ParamBindingIdCollector { pub struct BindingUsageFinder<'a, 'tcx> { cx: &'a LateContext<'tcx>, - binding_ids: Vec, + binding_ids: Vec, usage_found: bool, } impl<'a, 'tcx> BindingUsageFinder<'a, 'tcx> { @@ -129,16 +129,16 @@ impl<'a, 'tcx> BindingUsageFinder<'a, 'tcx> { finder.usage_found } } -impl<'a, 'tcx> intravisit::Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> { +impl<'a, 'tcx> Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { if !self.usage_found { intravisit::walk_expr(self, expr); } } - fn visit_path(&mut self, path: &hir::Path<'tcx>, _: hir::HirId) { + fn visit_path(&mut self, path: &hir::Path<'tcx>, _: HirId) { if let Res::Local(id) = path.res { if self.binding_ids.contains(&id) { self.usage_found = true; diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index 0a05ac029eae5..a3f3b32ed372b 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -180,9 +180,9 @@ pub fn for_each_expr_with_closures<'tcx, B, C: Continue>( } /// returns `true` if expr contains match expr desugared from try -fn contains_try(expr: &hir::Expr<'_>) -> bool { +fn contains_try(expr: &Expr<'_>) -> bool { for_each_expr(expr, |e| { - if matches!(e.kind, hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar(_))) { + if matches!(e.kind, ExprKind::Match(_, _, hir::MatchSource::TryDesugar(_))) { ControlFlow::Break(()) } else { ControlFlow::Continue(()) @@ -191,9 +191,9 @@ fn contains_try(expr: &hir::Expr<'_>) -> bool { .is_some() } -pub fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool +pub fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir Expr<'hir>, callback: F) -> bool where - F: FnMut(&'hir hir::Expr<'hir>) -> bool, + F: FnMut(&'hir Expr<'hir>) -> bool, { struct RetFinder { in_stmt: bool, @@ -236,37 +236,37 @@ where } } - impl<'hir, F: FnMut(&'hir hir::Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder { - fn visit_stmt(&mut self, stmt: &'hir hir::Stmt<'_>) { + impl<'hir, F: FnMut(&'hir Expr<'hir>) -> bool> Visitor<'hir> for RetFinder { + fn visit_stmt(&mut self, stmt: &'hir Stmt<'_>) { intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt); } - fn visit_expr(&mut self, expr: &'hir hir::Expr<'_>) { + fn visit_expr(&mut self, expr: &'hir Expr<'_>) { if self.failed { return; } if self.in_stmt { match expr.kind { - hir::ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr), - _ => intravisit::walk_expr(self, expr), + ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr), + _ => walk_expr(self, expr), } } else { match expr.kind { - hir::ExprKind::If(cond, then, else_opt) => { + ExprKind::If(cond, then, else_opt) => { self.inside_stmt(true).visit_expr(cond); self.visit_expr(then); if let Some(el) = else_opt { self.visit_expr(el); } }, - hir::ExprKind::Match(cond, arms, _) => { + ExprKind::Match(cond, arms, _) => { self.inside_stmt(true).visit_expr(cond); for arm in arms { self.visit_expr(arm.body); } }, - hir::ExprKind::Block(..) => intravisit::walk_expr(self, expr), - hir::ExprKind::Ret(Some(expr)) => self.visit_expr(expr), + ExprKind::Block(..) => walk_expr(self, expr), + ExprKind::Ret(Some(expr)) => self.visit_expr(expr), _ => self.failed |= !(self.cb)(expr), } } @@ -316,7 +316,7 @@ pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> is_const: bool, } impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> { - type NestedFilter = rustc_hir::intravisit::nested_filter::None; + type NestedFilter = intravisit::nested_filter::None; fn visit_expr(&mut self, e: &'tcx Expr<'_>) { if !self.is_const { diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs index 6644299816199..4251151c45431 100644 --- a/src/tools/clippy/lintcheck/src/main.rs +++ b/src/tools/clippy/lintcheck/src/main.rs @@ -5,6 +5,13 @@ // When a new lint is introduced, we can search the results for new warnings and check for false // positives. +#![warn( + trivial_casts, + trivial_numeric_casts, + rust_2018_idioms, + unused_lifetimes, + unused_qualifications +)] #![allow(clippy::collapsible_else_if)] mod config; @@ -189,13 +196,13 @@ impl CrateSource { // don't download/extract if we already have done so if !krate_file_path.is_file() { // create a file path to download and write the crate data into - let mut krate_dest = std::fs::File::create(&krate_file_path).unwrap(); + let mut krate_dest = fs::File::create(&krate_file_path).unwrap(); let mut krate_req = get(&url).unwrap().into_reader(); // copy the crate into the file - std::io::copy(&mut krate_req, &mut krate_dest).unwrap(); + io::copy(&mut krate_req, &mut krate_dest).unwrap(); // unzip the tarball - let ungz_tar = flate2::read::GzDecoder::new(std::fs::File::open(&krate_file_path).unwrap()); + let ungz_tar = flate2::read::GzDecoder::new(fs::File::open(&krate_file_path).unwrap()); // extract the tar archive let mut archive = tar::Archive::new(ungz_tar); archive.unpack(&extract_dir).expect("Failed to extract!"); @@ -257,7 +264,7 @@ impl CrateSource { }, CrateSource::Path { name, path, options } => { fn is_cache_dir(entry: &DirEntry) -> bool { - std::fs::read(entry.path().join("CACHEDIR.TAG")) + fs::read(entry.path().join("CACHEDIR.TAG")) .map(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55")) .unwrap_or(false) } @@ -268,7 +275,7 @@ impl CrateSource { let dest_crate_root = PathBuf::from(LINTCHECK_SOURCES).join(name); if dest_crate_root.exists() { println!("Deleting existing directory at {dest_crate_root:?}"); - std::fs::remove_dir_all(&dest_crate_root).unwrap(); + fs::remove_dir_all(&dest_crate_root).unwrap(); } println!("Copying {path:?} to {dest_crate_root:?}"); @@ -281,9 +288,9 @@ impl CrateSource { let metadata = entry_path.symlink_metadata().unwrap(); if metadata.is_dir() { - std::fs::create_dir(dest_path).unwrap(); + fs::create_dir(dest_path).unwrap(); } else if metadata.is_file() { - std::fs::copy(entry_path, dest_path).unwrap(); + fs::copy(entry_path, dest_path).unwrap(); } } @@ -330,7 +337,7 @@ impl Crate { ); } - let cargo_clippy_path = std::fs::canonicalize(cargo_clippy_path).unwrap(); + let cargo_clippy_path = fs::canonicalize(cargo_clippy_path).unwrap(); let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir"); @@ -353,7 +360,7 @@ impl Crate { clippy_args.push("--cap-lints=warn"); } else { clippy_args.push("--cap-lints=allow"); - clippy_args.extend(lint_filter.iter().map(std::string::String::as_str)); + clippy_args.extend(lint_filter.iter().map(String::as_str)); } if let Some(server) = server { @@ -454,7 +461,7 @@ fn build_clippy() { /// Read a `lintcheck_crates.toml` file fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { let toml_content: String = - std::fs::read_to_string(toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display())); + fs::read_to_string(toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display())); let crate_list: SourceList = toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{e}", toml_path.display())); // parse the hashmap of the toml file into a list of crates @@ -549,7 +556,7 @@ fn main() { } // assert that we launch lintcheck from the repo root (via cargo lintcheck) - if std::fs::metadata("lintcheck/Cargo.toml").is_err() { + if fs::metadata("lintcheck/Cargo.toml").is_err() { eprintln!("lintcheck needs to be run from clippy's repo root!\nUse `cargo lintcheck` alternatively."); std::process::exit(3); } @@ -570,7 +577,7 @@ fn main() { cargo_clippy_path.display() ); - let clippy_ver = std::process::Command::new(&cargo_clippy_path) + let clippy_ver = Command::new(&cargo_clippy_path) .arg("--version") .output() .map(|o| String::from_utf8_lossy(&o.stdout).into_owned()) @@ -699,7 +706,7 @@ fn main() { /// read the previous stats from the lintcheck-log file fn read_stats_from_file(file_path: &Path) -> HashMap { - let file_content: String = match std::fs::read_to_string(file_path).ok() { + let file_content: String = match fs::read_to_string(file_path).ok() { Some(content) => content, None => { return HashMap::new(); @@ -779,17 +786,17 @@ fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, us /// /// This function panics if creating one of the dirs fails. fn create_dirs(krate_download_dir: &Path, extract_dir: &Path) { - std::fs::create_dir("target/lintcheck/").unwrap_or_else(|err| { + fs::create_dir("target/lintcheck/").unwrap_or_else(|err| { assert_eq!( err.kind(), ErrorKind::AlreadyExists, "cannot create lintcheck target dir" ); }); - std::fs::create_dir(krate_download_dir).unwrap_or_else(|err| { + fs::create_dir(krate_download_dir).unwrap_or_else(|err| { assert_eq!(err.kind(), ErrorKind::AlreadyExists, "cannot create crate download dir"); }); - std::fs::create_dir(extract_dir).unwrap_or_else(|err| { + fs::create_dir(extract_dir).unwrap_or_else(|err| { assert_eq!( err.kind(), ErrorKind::AlreadyExists, @@ -816,7 +823,7 @@ fn lintcheck_test() { "--crates-toml", "lintcheck/test_sources.toml", ]; - let status = std::process::Command::new(env::var("CARGO").unwrap_or("cargo".into())) + let status = Command::new(env::var("CARGO").unwrap_or("cargo".into())) .args(args) .current_dir("..") // repo root .status(); diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index a63e66f3214c9..b2fe5c8bee7a5 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2024-03-21" +channel = "nightly-2024-04-04" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index a0c8bf9334c4e..32a31f5e08236 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -146,6 +146,8 @@ fn base_config(test_dir: &str) -> (Config, Args) { ); config.program.args.extend(EXTERN_FLAGS.iter().map(OsString::from)); + // Prevent rustc from creating `rustc-ice-*` files the console output is enough. + config.program.envs.push(("RUSTC_ICE".into(), Some("0".into()))); if let Some(host_libs) = option_env!("HOST_LIBS") { let dep = format!("-Ldependency={}", Path::new(host_libs).join("deps").display()); diff --git a/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.rs b/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.rs index 0e6a54452ee89..a828701bcee9f 100644 --- a/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.rs +++ b/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.rs @@ -3,7 +3,7 @@ //@revisions: allow_crates disallow_crates //@[allow_crates] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/absolute_paths/allow_crates //@[disallow_crates] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/absolute_paths/disallow_crates -#![allow(clippy::no_effect, unused)] +#![allow(clippy::no_effect, clippy::legacy_numeric_constants, unused)] #![warn(clippy::absolute_paths)] #![feature(decl_macro)] diff --git a/src/tools/clippy/tests/ui-toml/large_stack_frames/large_stack_frames.rs b/src/tools/clippy/tests/ui-toml/large_stack_frames/large_stack_frames.rs index 39798ffea4942..a612e56570ffa 100644 --- a/src/tools/clippy/tests/ui-toml/large_stack_frames/large_stack_frames.rs +++ b/src/tools/clippy/tests/ui-toml/large_stack_frames/large_stack_frames.rs @@ -10,7 +10,7 @@ fn f() { let _x = create_array::<1000>(); } fn f2() { - //~^ ERROR: this function allocates a large amount of stack space + //~^ ERROR: this function may allocate 1001 bytes on the stack let _x = create_array::<1001>(); } diff --git a/src/tools/clippy/tests/ui-toml/large_stack_frames/large_stack_frames.stderr b/src/tools/clippy/tests/ui-toml/large_stack_frames/large_stack_frames.stderr index c23fac1456469..19983e2f3e80d 100644 --- a/src/tools/clippy/tests/ui-toml/large_stack_frames/large_stack_frames.stderr +++ b/src/tools/clippy/tests/ui-toml/large_stack_frames/large_stack_frames.stderr @@ -1,13 +1,14 @@ -error: this function allocates a large amount of stack space - --> tests/ui-toml/large_stack_frames/large_stack_frames.rs:12:1 +error: this function may allocate 1001 bytes on the stack + --> tests/ui-toml/large_stack_frames/large_stack_frames.rs:12:4 | -LL | / fn f2() { -LL | | -LL | | let _x = create_array::<1001>(); -LL | | } - | |_^ +LL | fn f2() { + | ^^ +LL | +LL | let _x = create_array::<1001>(); + | -- `_x` is the largest part, at 1001 bytes for type `[u8; 1001]` | - = note: allocating large amounts of stack space can overflow the stack + = note: 1001 bytes is larger than Clippy's configured `stack-size-threshold` of 1000 + = note: allocating large amounts of stack space can overflow the stack and cause the program to abort = note: `-D clippy::large-stack-frames` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::large_stack_frames)]` diff --git a/src/tools/clippy/tests/ui/assigning_clones.fixed b/src/tools/clippy/tests/ui/assigning_clones.fixed index 160f3b9466316..8387c7d6156b5 100644 --- a/src/tools/clippy/tests/ui/assigning_clones.fixed +++ b/src/tools/clippy/tests/ui/assigning_clones.fixed @@ -153,6 +153,19 @@ fn clone_inside_macro() { clone_inside!(a, b); } +// Make sure that we don't suggest the lint when we call clone inside a Clone impl +// https://github.com/rust-lang/rust-clippy/issues/12600 +pub struct AvoidRecursiveCloneFrom; + +impl Clone for AvoidRecursiveCloneFrom { + fn clone(&self) -> Self { + Self + } + fn clone_from(&mut self, source: &Self) { + *self = source.clone(); + } +} + // ToOwned fn owned_method_mut_ref(mut_string: &mut String, ref_str: &str) { ref_str.clone_into(mut_string); diff --git a/src/tools/clippy/tests/ui/assigning_clones.rs b/src/tools/clippy/tests/ui/assigning_clones.rs index 14ba1d4db9a83..6f4da9f652c99 100644 --- a/src/tools/clippy/tests/ui/assigning_clones.rs +++ b/src/tools/clippy/tests/ui/assigning_clones.rs @@ -153,6 +153,19 @@ fn clone_inside_macro() { clone_inside!(a, b); } +// Make sure that we don't suggest the lint when we call clone inside a Clone impl +// https://github.com/rust-lang/rust-clippy/issues/12600 +pub struct AvoidRecursiveCloneFrom; + +impl Clone for AvoidRecursiveCloneFrom { + fn clone(&self) -> Self { + Self + } + fn clone_from(&mut self, source: &Self) { + *self = source.clone(); + } +} + // ToOwned fn owned_method_mut_ref(mut_string: &mut String, ref_str: &str) { *mut_string = ref_str.to_owned(); diff --git a/src/tools/clippy/tests/ui/assigning_clones.stderr b/src/tools/clippy/tests/ui/assigning_clones.stderr index ba59f067431a7..793927bd1cb10 100644 --- a/src/tools/clippy/tests/ui/assigning_clones.stderr +++ b/src/tools/clippy/tests/ui/assigning_clones.stderr @@ -86,37 +86,37 @@ LL | a = c.to_owned(); | ^^^^^^^^^^^^^^^^ help: use `clone_into()`: `c.clone_into(&mut a)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:158:5 + --> tests/ui/assigning_clones.rs:171:5 | LL | *mut_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(mut_string)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:162:5 + --> tests/ui/assigning_clones.rs:175:5 | LL | mut_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut mut_string)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:183:5 + --> tests/ui/assigning_clones.rs:196:5 | LL | **mut_box_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:187:5 + --> tests/ui/assigning_clones.rs:200:5 | LL | **mut_box_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:191:5 + --> tests/ui/assigning_clones.rs:204:5 | LL | *mut_thing = ToOwned::to_owned(ref_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, mut_thing)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:195:5 + --> tests/ui/assigning_clones.rs:208:5 | LL | mut_thing = ToOwned::to_owned(ref_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, &mut mut_thing)` diff --git a/src/tools/clippy/tests/ui/author/issue_3849.rs b/src/tools/clippy/tests/ui/author/issue_3849.rs index bae4570e539a1..5f65746d71f23 100644 --- a/src/tools/clippy/tests/ui/author/issue_3849.rs +++ b/src/tools/clippy/tests/ui/author/issue_3849.rs @@ -1,7 +1,7 @@ #![allow(dead_code)] #![allow(clippy::zero_ptr)] #![allow(clippy::transmute_ptr_to_ref)] -#![allow(clippy::transmuting_null)] +#![allow(clippy::transmuting_null, clippy::missing_transmute_annotations)] pub const ZPTR: *const usize = 0 as *const _; diff --git a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs index 6b164967a28e4..9efbb39084976 100644 --- a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs +++ b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs @@ -50,3 +50,10 @@ macro_rules! macro_with_panic { panic!() }; } + +#[macro_export] +macro_rules! bad_transmute { + ($e:expr) => { + std::mem::transmute($e) + }; +} diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions.fixed b/src/tools/clippy/tests/ui/blocks_in_conditions.fixed index caf29e23d5450..a2da5f9c5fb98 100644 --- a/src/tools/clippy/tests/ui/blocks_in_conditions.fixed +++ b/src/tools/clippy/tests/ui/blocks_in_conditions.fixed @@ -1,7 +1,12 @@ //@aux-build:proc_macro_attr.rs #![warn(clippy::blocks_in_conditions)] -#![allow(unused, clippy::let_and_return, clippy::needless_if)] +#![allow( + unused, + clippy::let_and_return, + clippy::needless_if, + clippy::missing_transmute_annotations +)] #![warn(clippy::nonminimal_bool)] macro_rules! blocky { diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions.rs b/src/tools/clippy/tests/ui/blocks_in_conditions.rs index e72daaa910d43..608ca4cf267f6 100644 --- a/src/tools/clippy/tests/ui/blocks_in_conditions.rs +++ b/src/tools/clippy/tests/ui/blocks_in_conditions.rs @@ -1,7 +1,12 @@ //@aux-build:proc_macro_attr.rs #![warn(clippy::blocks_in_conditions)] -#![allow(unused, clippy::let_and_return, clippy::needless_if)] +#![allow( + unused, + clippy::let_and_return, + clippy::needless_if, + clippy::missing_transmute_annotations +)] #![warn(clippy::nonminimal_bool)] macro_rules! blocky { diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions.stderr b/src/tools/clippy/tests/ui/blocks_in_conditions.stderr index 3641e71aae830..a55e1efb575ea 100644 --- a/src/tools/clippy/tests/ui/blocks_in_conditions.stderr +++ b/src/tools/clippy/tests/ui/blocks_in_conditions.stderr @@ -1,5 +1,5 @@ error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> tests/ui/blocks_in_conditions.rs:25:5 + --> tests/ui/blocks_in_conditions.rs:30:5 | LL | / if { LL | | @@ -20,13 +20,13 @@ LL ~ }; if res { | error: omit braces around single expression condition - --> tests/ui/blocks_in_conditions.rs:37:8 + --> tests/ui/blocks_in_conditions.rs:42:8 | LL | if { true } { 6 } else { 10 } | ^^^^^^^^ help: try: `true` error: this boolean expression can be simplified - --> tests/ui/blocks_in_conditions.rs:43:8 + --> tests/ui/blocks_in_conditions.rs:48:8 | LL | if true && x == 3 { 6 } else { 10 } | ^^^^^^^^^^^^^^ help: try: `x == 3` @@ -35,7 +35,7 @@ LL | if true && x == 3 { 6 } else { 10 } = help: to override `-D warnings` add `#[allow(clippy::nonminimal_bool)]` error: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> tests/ui/blocks_in_conditions.rs:70:5 + --> tests/ui/blocks_in_conditions.rs:75:5 | LL | / match { LL | | diff --git a/src/tools/clippy/tests/ui/box_default.fixed b/src/tools/clippy/tests/ui/box_default.fixed index fea7405c68586..6c2896b3aa0f2 100644 --- a/src/tools/clippy/tests/ui/box_default.fixed +++ b/src/tools/clippy/tests/ui/box_default.fixed @@ -1,5 +1,5 @@ #![warn(clippy::box_default)] -#![allow(clippy::default_constructed_unit_structs)] +#![allow(clippy::boxed_local, clippy::default_constructed_unit_structs)] #[derive(Default)] struct ImplementsDefault; @@ -12,26 +12,50 @@ impl OwnDefault { } } -macro_rules! outer { - ($e: expr) => { - $e +macro_rules! default { + () => { + Default::default() + }; +} + +macro_rules! string_new { + () => { + String::new() + }; +} + +macro_rules! box_new { + ($e:expr) => { + Box::new($e) }; } fn main() { - let _string: Box = Box::default(); - let _byte = Box::::default(); - let _vec = Box::>::default(); - let _impl = Box::::default(); - let _impl2 = Box::::default(); - let _impl3: Box = Box::default(); - let _own = Box::new(OwnDefault::default()); // should not lint - let _in_macro = outer!(Box::::default()); - let _string_default = outer!(Box::::default()); - let _vec2: Box> = Box::default(); - let _vec3: Box> = Box::default(); - let _vec4: Box<_> = Box::>::default(); - let _more = ret_ty_fn(); + let string1: Box = Box::default(); + let string2: Box = Box::default(); + let impl1: Box = Box::default(); + let vec: Box> = Box::default(); + let byte: Box = Box::default(); + let vec2: Box> = Box::default(); + let vec3: Box> = Box::default(); + + let plain_default = Box::default(); + let _: Box = plain_default; + + let _: Box = Box::new(default!()); + let _: Box = Box::new(string_new!()); + let _: Box = box_new!(Default::default()); + let _: Box = box_new!(String::new()); + let _: Box = box_new!(default!()); + let _: Box = box_new!(string_new!()); + + let own: Box = Box::new(OwnDefault::default()); // should not lint + + // Do not suggest where a turbofish would be required + let impl2 = Box::new(ImplementsDefault::default()); + let impl3 = Box::new(::default()); + let vec4: Box<_> = Box::new(Vec::from([false; 0])); + let more = ret_ty_fn(); call_ty_fn(Box::default()); issue_10381(); @@ -44,10 +68,9 @@ fn main() { } fn ret_ty_fn() -> Box { - Box::::default() + Box::new(bool::default()) // Could lint, currently doesn't } -#[allow(clippy::boxed_local)] fn call_ty_fn(_b: Box) { issue_9621_dyn_trait(); } @@ -61,7 +84,7 @@ impl Read for ImplementsDefault { } fn issue_9621_dyn_trait() { - let _: Box = Box::::default(); + let _: Box = Box::new(ImplementsDefault::default()); issue_10089(); } @@ -70,7 +93,7 @@ fn issue_10089() { #[derive(Default)] struct WeirdPathed; - let _ = Box::::default(); + let _ = Box::new(WeirdPathed::default()); }; } @@ -82,7 +105,7 @@ fn issue_10381() { fn maybe_get_bar(i: u32) -> Option> { if i % 2 == 0 { - Some(Box::::default()) + Some(Box::new(Foo::default())) } else { None } @@ -91,20 +114,6 @@ fn issue_10381() { assert!(maybe_get_bar(2).is_some()); } -#[allow(unused)] -fn issue_11868() { - fn foo(_: &mut Vec) {} - - macro_rules! bar { - ($baz:expr) => { - Box::leak(Box::new($baz)) - }; - } - - foo(bar!(vec![])); - foo(bar!(vec![1])); -} - // Issue #11927: The quickfix for the `Box::new` suggests replacing with `Box::::default()`, // removing the `outer::` segment. fn issue_11927() { @@ -116,7 +125,7 @@ fn issue_11927() { } fn foo() { - let _b = Box::::default(); - let _b = Box::>::default(); + let _b = Box::new(outer::Inner::default()); + let _b = Box::new(std::collections::HashSet::::new()); } } diff --git a/src/tools/clippy/tests/ui/box_default.rs b/src/tools/clippy/tests/ui/box_default.rs index eecba7464ec2f..e19a62a90221e 100644 --- a/src/tools/clippy/tests/ui/box_default.rs +++ b/src/tools/clippy/tests/ui/box_default.rs @@ -1,5 +1,5 @@ #![warn(clippy::box_default)] -#![allow(clippy::default_constructed_unit_structs)] +#![allow(clippy::boxed_local, clippy::default_constructed_unit_structs)] #[derive(Default)] struct ImplementsDefault; @@ -12,26 +12,50 @@ impl OwnDefault { } } -macro_rules! outer { - ($e: expr) => { - $e +macro_rules! default { + () => { + Default::default() + }; +} + +macro_rules! string_new { + () => { + String::new() + }; +} + +macro_rules! box_new { + ($e:expr) => { + Box::new($e) }; } fn main() { - let _string: Box = Box::new(Default::default()); - let _byte = Box::new(u8::default()); - let _vec = Box::new(Vec::::new()); - let _impl = Box::new(ImplementsDefault::default()); - let _impl2 = Box::new(::default()); - let _impl3: Box = Box::new(Default::default()); - let _own = Box::new(OwnDefault::default()); // should not lint - let _in_macro = outer!(Box::new(String::new())); - let _string_default = outer!(Box::new(String::from(""))); - let _vec2: Box> = Box::new(vec![]); - let _vec3: Box> = Box::new(Vec::from([])); - let _vec4: Box<_> = Box::new(Vec::from([false; 0])); - let _more = ret_ty_fn(); + let string1: Box = Box::new(Default::default()); + let string2: Box = Box::new(String::new()); + let impl1: Box = Box::new(Default::default()); + let vec: Box> = Box::new(Vec::new()); + let byte: Box = Box::new(u8::default()); + let vec2: Box> = Box::new(vec![]); + let vec3: Box> = Box::new(Vec::from([])); + + let plain_default = Box::new(Default::default()); + let _: Box = plain_default; + + let _: Box = Box::new(default!()); + let _: Box = Box::new(string_new!()); + let _: Box = box_new!(Default::default()); + let _: Box = box_new!(String::new()); + let _: Box = box_new!(default!()); + let _: Box = box_new!(string_new!()); + + let own: Box = Box::new(OwnDefault::default()); // should not lint + + // Do not suggest where a turbofish would be required + let impl2 = Box::new(ImplementsDefault::default()); + let impl3 = Box::new(::default()); + let vec4: Box<_> = Box::new(Vec::from([false; 0])); + let more = ret_ty_fn(); call_ty_fn(Box::new(u8::default())); issue_10381(); @@ -44,10 +68,9 @@ fn main() { } fn ret_ty_fn() -> Box { - Box::new(bool::default()) + Box::new(bool::default()) // Could lint, currently doesn't } -#[allow(clippy::boxed_local)] fn call_ty_fn(_b: Box) { issue_9621_dyn_trait(); } @@ -91,20 +114,6 @@ fn issue_10381() { assert!(maybe_get_bar(2).is_some()); } -#[allow(unused)] -fn issue_11868() { - fn foo(_: &mut Vec) {} - - macro_rules! bar { - ($baz:expr) => { - Box::leak(Box::new($baz)) - }; - } - - foo(bar!(vec![])); - foo(bar!(vec![1])); -} - // Issue #11927: The quickfix for the `Box::new` suggests replacing with `Box::::default()`, // removing the `outer::` segment. fn issue_11927() { diff --git a/src/tools/clippy/tests/ui/box_default.stderr b/src/tools/clippy/tests/ui/box_default.stderr index 8bb5917a627b7..f172a875dce47 100644 --- a/src/tools/clippy/tests/ui/box_default.stderr +++ b/src/tools/clippy/tests/ui/box_default.stderr @@ -1,113 +1,59 @@ error: `Box::new(_)` of default value - --> tests/ui/box_default.rs:22:32 + --> tests/ui/box_default.rs:34:32 | -LL | let _string: Box = Box::new(Default::default()); +LL | let string1: Box = Box::new(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` | = note: `-D clippy::box-default` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::box_default)]` error: `Box::new(_)` of default value - --> tests/ui/box_default.rs:23:17 + --> tests/ui/box_default.rs:35:32 | -LL | let _byte = Box::new(u8::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` +LL | let string2: Box = Box::new(String::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` error: `Box::new(_)` of default value - --> tests/ui/box_default.rs:24:16 + --> tests/ui/box_default.rs:36:41 | -LL | let _vec = Box::new(Vec::::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::>::default()` +LL | let impl1: Box = Box::new(Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` error: `Box::new(_)` of default value - --> tests/ui/box_default.rs:25:17 + --> tests/ui/box_default.rs:37:29 | -LL | let _impl = Box::new(ImplementsDefault::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` +LL | let vec: Box> = Box::new(Vec::new()); + | ^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` error: `Box::new(_)` of default value - --> tests/ui/box_default.rs:26:18 + --> tests/ui/box_default.rs:38:25 | -LL | let _impl2 = Box::new(::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` +LL | let byte: Box = Box::new(u8::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` error: `Box::new(_)` of default value - --> tests/ui/box_default.rs:27:42 + --> tests/ui/box_default.rs:39:45 | -LL | let _impl3: Box = Box::new(Default::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` +LL | let vec2: Box> = Box::new(vec![]); + | ^^^^^^^^^^^^^^^^ help: try: `Box::default()` error: `Box::new(_)` of default value - --> tests/ui/box_default.rs:29:28 + --> tests/ui/box_default.rs:40:32 | -LL | let _in_macro = outer!(Box::new(String::new())); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` +LL | let vec3: Box> = Box::new(Vec::from([])); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` error: `Box::new(_)` of default value - --> tests/ui/box_default.rs:30:34 + --> tests/ui/box_default.rs:42:25 | -LL | let _string_default = outer!(Box::new(String::from(""))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` +LL | let plain_default = Box::new(Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` error: `Box::new(_)` of default value - --> tests/ui/box_default.rs:31:46 - | -LL | let _vec2: Box> = Box::new(vec![]); - | ^^^^^^^^^^^^^^^^ help: try: `Box::default()` - -error: `Box::new(_)` of default value - --> tests/ui/box_default.rs:32:33 - | -LL | let _vec3: Box> = Box::new(Vec::from([])); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` - -error: `Box::new(_)` of default value - --> tests/ui/box_default.rs:33:25 - | -LL | let _vec4: Box<_> = Box::new(Vec::from([false; 0])); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::>::default()` - -error: `Box::new(_)` of default value - --> tests/ui/box_default.rs:35:16 + --> tests/ui/box_default.rs:59:16 | LL | call_ty_fn(Box::new(u8::default())); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` -error: `Box::new(_)` of default value - --> tests/ui/box_default.rs:47:5 - | -LL | Box::new(bool::default()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` - -error: `Box::new(_)` of default value - --> tests/ui/box_default.rs:64:28 - | -LL | let _: Box = Box::new(ImplementsDefault::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` - -error: `Box::new(_)` of default value - --> tests/ui/box_default.rs:73:17 - | -LL | let _ = Box::new(WeirdPathed::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` - -error: `Box::new(_)` of default value - --> tests/ui/box_default.rs:85:18 - | -LL | Some(Box::new(Foo::default())) - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` - -error: `Box::new(_)` of default value - --> tests/ui/box_default.rs:119:18 - | -LL | let _b = Box::new(outer::Inner::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` - -error: `Box::new(_)` of default value - --> tests/ui/box_default.rs:120:18 - | -LL | let _b = Box::new(std::collections::HashSet::::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::>::default()` - -error: aborting due to 18 previous errors +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/cast.rs b/src/tools/clippy/tests/ui/cast.rs index a7331ddc7d0e7..ce76ad3d3ad12 100644 --- a/src/tools/clippy/tests/ui/cast.rs +++ b/src/tools/clippy/tests/ui/cast.rs @@ -9,7 +9,12 @@ clippy::cast_sign_loss, clippy::cast_possible_wrap )] -#![allow(clippy::cast_abs_to_unsigned, clippy::no_effect, clippy::unnecessary_operation)] +#![allow( + clippy::cast_abs_to_unsigned, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::unnecessary_literal_unwrap +)] fn main() { // Test clippy::cast_precision_loss @@ -457,3 +462,8 @@ fn issue11642() { //~^ ERROR: casting `i32` to `u32` may lose the sign of the value } } + +fn issue12506() -> usize { + let bar: Result, u32> = Ok(Some(10)); + bar.unwrap().unwrap() as usize +} diff --git a/src/tools/clippy/tests/ui/cast.stderr b/src/tools/clippy/tests/ui/cast.stderr index dd5d339b81b2a..3736e8aee0af1 100644 --- a/src/tools/clippy/tests/ui/cast.stderr +++ b/src/tools/clippy/tests/ui/cast.stderr @@ -1,5 +1,5 @@ error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> tests/ui/cast.rs:17:5 + --> tests/ui/cast.rs:22:5 | LL | x0 as f32; | ^^^^^^^^^ @@ -8,37 +8,37 @@ LL | x0 as f32; = help: to override `-D warnings` add `#[allow(clippy::cast_precision_loss)]` error: casting `i64` to `f32` causes a loss of precision (`i64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> tests/ui/cast.rs:21:5 + --> tests/ui/cast.rs:26:5 | LL | x1 as f32; | ^^^^^^^^^ error: casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) - --> tests/ui/cast.rs:23:5 + --> tests/ui/cast.rs:28:5 | LL | x1 as f64; | ^^^^^^^^^ error: casting `u32` to `f32` causes a loss of precision (`u32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> tests/ui/cast.rs:26:5 + --> tests/ui/cast.rs:31:5 | LL | x2 as f32; | ^^^^^^^^^ error: casting `u64` to `f32` causes a loss of precision (`u64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> tests/ui/cast.rs:29:5 + --> tests/ui/cast.rs:34:5 | LL | x3 as f32; | ^^^^^^^^^ error: casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) - --> tests/ui/cast.rs:31:5 + --> tests/ui/cast.rs:36:5 | LL | x3 as f64; | ^^^^^^^^^ error: casting `f32` to `i32` may truncate the value - --> tests/ui/cast.rs:34:5 + --> tests/ui/cast.rs:39:5 | LL | 1f32 as i32; | ^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL | 1f32 as i32; = help: to override `-D warnings` add `#[allow(clippy::cast_possible_truncation)]` error: casting `f32` to `u32` may truncate the value - --> tests/ui/cast.rs:36:5 + --> tests/ui/cast.rs:41:5 | LL | 1f32 as u32; | ^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | 1f32 as u32; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `f32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:36:5 + --> tests/ui/cast.rs:41:5 | LL | 1f32 as u32; | ^^^^^^^^^^^ @@ -65,7 +65,7 @@ LL | 1f32 as u32; = help: to override `-D warnings` add `#[allow(clippy::cast_sign_loss)]` error: casting `f64` to `f32` may truncate the value - --> tests/ui/cast.rs:40:5 + --> tests/ui/cast.rs:45:5 | LL | 1f64 as f32; | ^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL | 1f64 as f32; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `i32` to `i8` may truncate the value - --> tests/ui/cast.rs:42:5 + --> tests/ui/cast.rs:47:5 | LL | 1i32 as i8; | ^^^^^^^^^^ @@ -85,7 +85,7 @@ LL | i8::try_from(1i32); | ~~~~~~~~~~~~~~~~~~ error: casting `i32` to `u8` may truncate the value - --> tests/ui/cast.rs:44:5 + --> tests/ui/cast.rs:49:5 | LL | 1i32 as u8; | ^^^^^^^^^^ @@ -97,7 +97,7 @@ LL | u8::try_from(1i32); | ~~~~~~~~~~~~~~~~~~ error: casting `f64` to `isize` may truncate the value - --> tests/ui/cast.rs:46:5 + --> tests/ui/cast.rs:51:5 | LL | 1f64 as isize; | ^^^^^^^^^^^^^ @@ -105,7 +105,7 @@ LL | 1f64 as isize; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `f64` to `usize` may truncate the value - --> tests/ui/cast.rs:48:5 + --> tests/ui/cast.rs:53:5 | LL | 1f64 as usize; | ^^^^^^^^^^^^^ @@ -113,13 +113,13 @@ LL | 1f64 as usize; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `f64` to `usize` may lose the sign of the value - --> tests/ui/cast.rs:48:5 + --> tests/ui/cast.rs:53:5 | LL | 1f64 as usize; | ^^^^^^^^^^^^^ error: casting `u32` to `u16` may truncate the value - --> tests/ui/cast.rs:51:5 + --> tests/ui/cast.rs:56:5 | LL | 1f32 as u32 as u16; | ^^^^^^^^^^^^^^^^^^ @@ -131,7 +131,7 @@ LL | u16::try_from(1f32 as u32); | ~~~~~~~~~~~~~~~~~~~~~~~~~~ error: casting `f32` to `u32` may truncate the value - --> tests/ui/cast.rs:51:5 + --> tests/ui/cast.rs:56:5 | LL | 1f32 as u32 as u16; | ^^^^^^^^^^^ @@ -139,13 +139,13 @@ LL | 1f32 as u32 as u16; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `f32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:51:5 + --> tests/ui/cast.rs:56:5 | LL | 1f32 as u32 as u16; | ^^^^^^^^^^^ error: casting `i32` to `i8` may truncate the value - --> tests/ui/cast.rs:56:22 + --> tests/ui/cast.rs:61:22 | LL | let _x: i8 = 1i32 as _; | ^^^^^^^^^ @@ -157,7 +157,7 @@ LL | let _x: i8 = 1i32.try_into(); | ~~~~~~~~~~~~~~~ error: casting `f32` to `i32` may truncate the value - --> tests/ui/cast.rs:58:9 + --> tests/ui/cast.rs:63:9 | LL | 1f32 as i32; | ^^^^^^^^^^^ @@ -165,7 +165,7 @@ LL | 1f32 as i32; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `f64` to `i32` may truncate the value - --> tests/ui/cast.rs:60:9 + --> tests/ui/cast.rs:65:9 | LL | 1f64 as i32; | ^^^^^^^^^^^ @@ -173,7 +173,7 @@ LL | 1f64 as i32; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `f32` to `u8` may truncate the value - --> tests/ui/cast.rs:62:9 + --> tests/ui/cast.rs:67:9 | LL | 1f32 as u8; | ^^^^^^^^^^ @@ -181,13 +181,13 @@ LL | 1f32 as u8; = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... error: casting `f32` to `u8` may lose the sign of the value - --> tests/ui/cast.rs:62:9 + --> tests/ui/cast.rs:67:9 | LL | 1f32 as u8; | ^^^^^^^^^^ error: casting `u8` to `i8` may wrap around the value - --> tests/ui/cast.rs:67:5 + --> tests/ui/cast.rs:72:5 | LL | 1u8 as i8; | ^^^^^^^^^ @@ -196,31 +196,31 @@ LL | 1u8 as i8; = help: to override `-D warnings` add `#[allow(clippy::cast_possible_wrap)]` error: casting `u16` to `i16` may wrap around the value - --> tests/ui/cast.rs:70:5 + --> tests/ui/cast.rs:75:5 | LL | 1u16 as i16; | ^^^^^^^^^^^ error: casting `u32` to `i32` may wrap around the value - --> tests/ui/cast.rs:72:5 + --> tests/ui/cast.rs:77:5 | LL | 1u32 as i32; | ^^^^^^^^^^^ error: casting `u64` to `i64` may wrap around the value - --> tests/ui/cast.rs:74:5 + --> tests/ui/cast.rs:79:5 | LL | 1u64 as i64; | ^^^^^^^^^^^ error: casting `usize` to `isize` may wrap around the value - --> tests/ui/cast.rs:76:5 + --> tests/ui/cast.rs:81:5 | LL | 1usize as isize; | ^^^^^^^^^^^^^^^ error: casting `usize` to `i8` may truncate the value - --> tests/ui/cast.rs:79:5 + --> tests/ui/cast.rs:84:5 | LL | 1usize as i8; | ^^^^^^^^^^^^ @@ -232,7 +232,7 @@ LL | i8::try_from(1usize); | ~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `i16` may truncate the value - --> tests/ui/cast.rs:82:5 + --> tests/ui/cast.rs:87:5 | LL | 1usize as i16; | ^^^^^^^^^^^^^ @@ -244,7 +244,7 @@ LL | i16::try_from(1usize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `i16` may wrap around the value on targets with 16-bit wide pointers - --> tests/ui/cast.rs:82:5 + --> tests/ui/cast.rs:87:5 | LL | 1usize as i16; | ^^^^^^^^^^^^^ @@ -253,7 +253,7 @@ LL | 1usize as i16; = note: for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers - --> tests/ui/cast.rs:87:5 + --> tests/ui/cast.rs:92:5 | LL | 1usize as i32; | ^^^^^^^^^^^^^ @@ -265,19 +265,19 @@ LL | i32::try_from(1usize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers - --> tests/ui/cast.rs:87:5 + --> tests/ui/cast.rs:92:5 | LL | 1usize as i32; | ^^^^^^^^^^^^^ error: casting `usize` to `i64` may wrap around the value on targets with 64-bit wide pointers - --> tests/ui/cast.rs:91:5 + --> tests/ui/cast.rs:96:5 | LL | 1usize as i64; | ^^^^^^^^^^^^^ error: casting `u16` to `isize` may wrap around the value on targets with 16-bit wide pointers - --> tests/ui/cast.rs:96:5 + --> tests/ui/cast.rs:101:5 | LL | 1u16 as isize; | ^^^^^^^^^^^^^ @@ -286,13 +286,13 @@ LL | 1u16 as isize; = note: for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers - --> tests/ui/cast.rs:100:5 + --> tests/ui/cast.rs:105:5 | LL | 1u32 as isize; | ^^^^^^^^^^^^^ error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers - --> tests/ui/cast.rs:103:5 + --> tests/ui/cast.rs:108:5 | LL | 1u64 as isize; | ^^^^^^^^^^^^^ @@ -304,55 +304,55 @@ LL | isize::try_from(1u64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers - --> tests/ui/cast.rs:103:5 + --> tests/ui/cast.rs:108:5 | LL | 1u64 as isize; | ^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:108:5 + --> tests/ui/cast.rs:113:5 | LL | -1i32 as u32; | ^^^^^^^^^^^^ error: casting `isize` to `usize` may lose the sign of the value - --> tests/ui/cast.rs:111:5 + --> tests/ui/cast.rs:116:5 | LL | -1isize as usize; | ^^^^^^^^^^^^^^^^ error: casting `i8` to `u8` may lose the sign of the value - --> tests/ui/cast.rs:122:5 + --> tests/ui/cast.rs:127:5 | LL | (i8::MIN).abs() as u8; | ^^^^^^^^^^^^^^^^^^^^^ error: casting `i64` to `u64` may lose the sign of the value - --> tests/ui/cast.rs:126:5 + --> tests/ui/cast.rs:131:5 | LL | (-1i64).abs() as u64; | ^^^^^^^^^^^^^^^^^^^^ error: casting `isize` to `usize` may lose the sign of the value - --> tests/ui/cast.rs:127:5 + --> tests/ui/cast.rs:132:5 | LL | (-1isize).abs() as usize; | ^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i64` to `u64` may lose the sign of the value - --> tests/ui/cast.rs:134:5 + --> tests/ui/cast.rs:139:5 | LL | (unsafe { (-1i64).checked_abs().unwrap_unchecked() }) as u64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i64` to `u64` may lose the sign of the value - --> tests/ui/cast.rs:149:5 + --> tests/ui/cast.rs:154:5 | LL | (unsafe { (-1i64).checked_isqrt().unwrap_unchecked() }) as u64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i64` to `i8` may truncate the value - --> tests/ui/cast.rs:200:5 + --> tests/ui/cast.rs:205:5 | LL | (-99999999999i64).min(1) as i8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -364,7 +364,7 @@ LL | i8::try_from((-99999999999i64).min(1)); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: casting `u64` to `u8` may truncate the value - --> tests/ui/cast.rs:214:5 + --> tests/ui/cast.rs:219:5 | LL | 999999u64.clamp(0, 256) as u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -376,7 +376,7 @@ LL | u8::try_from(999999u64.clamp(0, 256)); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: casting `main::E2` to `u8` may truncate the value - --> tests/ui/cast.rs:237:21 + --> tests/ui/cast.rs:242:21 | LL | let _ = self as u8; | ^^^^^^^^^^ @@ -388,7 +388,7 @@ LL | let _ = u8::try_from(self); | ~~~~~~~~~~~~~~~~~~ error: casting `main::E2::B` to `u8` will truncate the value - --> tests/ui/cast.rs:239:21 + --> tests/ui/cast.rs:244:21 | LL | let _ = Self::B as u8; | ^^^^^^^^^^^^^ @@ -397,7 +397,7 @@ LL | let _ = Self::B as u8; = help: to override `-D warnings` add `#[allow(clippy::cast_enum_truncation)]` error: casting `main::E5` to `i8` may truncate the value - --> tests/ui/cast.rs:281:21 + --> tests/ui/cast.rs:286:21 | LL | let _ = self as i8; | ^^^^^^^^^^ @@ -409,13 +409,13 @@ LL | let _ = i8::try_from(self); | ~~~~~~~~~~~~~~~~~~ error: casting `main::E5::A` to `i8` will truncate the value - --> tests/ui/cast.rs:283:21 + --> tests/ui/cast.rs:288:21 | LL | let _ = Self::A as i8; | ^^^^^^^^^^^^^ error: casting `main::E6` to `i16` may truncate the value - --> tests/ui/cast.rs:300:21 + --> tests/ui/cast.rs:305:21 | LL | let _ = self as i16; | ^^^^^^^^^^^ @@ -427,7 +427,7 @@ LL | let _ = i16::try_from(self); | ~~~~~~~~~~~~~~~~~~~ error: casting `main::E7` to `usize` may truncate the value on targets with 32-bit wide pointers - --> tests/ui/cast.rs:319:21 + --> tests/ui/cast.rs:324:21 | LL | let _ = self as usize; | ^^^^^^^^^^^^^ @@ -439,7 +439,7 @@ LL | let _ = usize::try_from(self); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `main::E10` to `u16` may truncate the value - --> tests/ui/cast.rs:366:21 + --> tests/ui/cast.rs:371:21 | LL | let _ = self as u16; | ^^^^^^^^^^^ @@ -451,7 +451,7 @@ LL | let _ = u16::try_from(self); | ~~~~~~~~~~~~~~~~~~~ error: casting `u32` to `u8` may truncate the value - --> tests/ui/cast.rs:377:13 + --> tests/ui/cast.rs:382:13 | LL | let c = (q >> 16) as u8; | ^^^^^^^^^^^^^^^ @@ -463,7 +463,7 @@ LL | let c = u8::try_from(q >> 16); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u32` to `u8` may truncate the value - --> tests/ui/cast.rs:381:13 + --> tests/ui/cast.rs:386:13 | LL | let c = (q / 1000) as u8; | ^^^^^^^^^^^^^^^^ @@ -475,85 +475,85 @@ LL | let c = u8::try_from(q / 1000); | ~~~~~~~~~~~~~~~~~~~~~~ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:393:9 + --> tests/ui/cast.rs:398:9 | LL | (x * x) as u32; | ^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:398:32 + --> tests/ui/cast.rs:403:32 | LL | let _a = |x: i32| -> u32 { (x * x * x * x) as u32 }; | ^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:400:5 + --> tests/ui/cast.rs:405:5 | LL | (2_i32).checked_pow(3).unwrap() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:401:5 + --> tests/ui/cast.rs:406:5 | LL | (-2_i32).pow(3) as u32; | ^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:406:5 + --> tests/ui/cast.rs:411:5 | LL | (-5_i32 % 2) as u32; | ^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:408:5 + --> tests/ui/cast.rs:413:5 | LL | (-5_i32 % -2) as u32; | ^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:411:5 + --> tests/ui/cast.rs:416:5 | LL | (-2_i32 >> 1) as u32; | ^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:415:5 + --> tests/ui/cast.rs:420:5 | LL | (x * x) as u32; | ^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:416:5 + --> tests/ui/cast.rs:421:5 | LL | (x * x * x) as u32; | ^^^^^^^^^^^^^^^^^^ error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:420:5 + --> tests/ui/cast.rs:425:5 | LL | (y * y * y * y * -2) as u16; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:422:5 + --> tests/ui/cast.rs:427:5 | LL | (y * y * y / y * 2) as u16; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:423:5 + --> tests/ui/cast.rs:428:5 | LL | (y * y / y * 2) as u16; | ^^^^^^^^^^^^^^^^^^^^^^ error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:425:5 + --> tests/ui/cast.rs:430:5 | LL | (y / y * y * -2) as u16; | ^^^^^^^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `/` - --> tests/ui/cast.rs:425:6 + --> tests/ui/cast.rs:430:6 | LL | (y / y * y * -2) as u16; | ^^^^^ @@ -561,94 +561,112 @@ LL | (y / y * y * -2) as u16; = note: `#[deny(clippy::eq_op)]` on by default error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:428:5 + --> tests/ui/cast.rs:433:5 | LL | (y + y + y + -2) as u16; | ^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:430:5 + --> tests/ui/cast.rs:435:5 | LL | (y + y + y + 2) as u16; | ^^^^^^^^^^^^^^^^^^^^^^ error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:434:5 + --> tests/ui/cast.rs:439:5 | LL | (z + -2) as u16; | ^^^^^^^^^^^^^^^ error: casting `i16` to `u16` may lose the sign of the value - --> tests/ui/cast.rs:436:5 + --> tests/ui/cast.rs:441:5 | LL | (z + z + 2) as u16; | ^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:439:9 + --> tests/ui/cast.rs:444:9 | LL | (a * a * b * b * c * c) as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:440:9 + --> tests/ui/cast.rs:445:9 | LL | (a * b * c) as u32; | ^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:442:9 + --> tests/ui/cast.rs:447:9 | LL | (a * -b * c) as u32; | ^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:444:9 + --> tests/ui/cast.rs:449:9 | LL | (a * b * c * c) as u32; | ^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:445:9 + --> tests/ui/cast.rs:450:9 | LL | (a * -2) as u32; | ^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:447:9 + --> tests/ui/cast.rs:452:9 | LL | (a * b * c * -2) as u32; | ^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:449:9 + --> tests/ui/cast.rs:454:9 | LL | (a / b) as u32; | ^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:450:9 + --> tests/ui/cast.rs:455:9 | LL | (a / b * c) as u32; | ^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:452:9 + --> tests/ui/cast.rs:457:9 | LL | (a / b + b * c) as u32; | ^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:454:9 + --> tests/ui/cast.rs:459:9 | LL | a.saturating_pow(3) as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> tests/ui/cast.rs:456:9 + --> tests/ui/cast.rs:461:9 | LL | (a.abs() * b.pow(2) / c.abs()) as u32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 85 previous errors +error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers + --> tests/ui/cast.rs:468:5 + | +LL | bar.unwrap().unwrap() as usize + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | usize::try_from(bar.unwrap().unwrap()) + | + +error: casting `i64` to `usize` may lose the sign of the value + --> tests/ui/cast.rs:468:5 + | +LL | bar.unwrap().unwrap() as usize + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 87 previous errors diff --git a/src/tools/clippy/tests/ui/checked_conversions.fixed b/src/tools/clippy/tests/ui/checked_conversions.fixed index 0e05a27429b0c..1e8da33161414 100644 --- a/src/tools/clippy/tests/ui/checked_conversions.fixed +++ b/src/tools/clippy/tests/ui/checked_conversions.fixed @@ -1,5 +1,6 @@ #![allow( clippy::cast_lossless, + clippy::legacy_numeric_constants, unused, // Int::max_value will be deprecated in the future deprecated, diff --git a/src/tools/clippy/tests/ui/checked_conversions.rs b/src/tools/clippy/tests/ui/checked_conversions.rs index ac78269926535..67a9adc049ead 100644 --- a/src/tools/clippy/tests/ui/checked_conversions.rs +++ b/src/tools/clippy/tests/ui/checked_conversions.rs @@ -1,5 +1,6 @@ #![allow( clippy::cast_lossless, + clippy::legacy_numeric_constants, unused, // Int::max_value will be deprecated in the future deprecated, diff --git a/src/tools/clippy/tests/ui/checked_conversions.stderr b/src/tools/clippy/tests/ui/checked_conversions.stderr index 223e379cce960..453cd7fcf0169 100644 --- a/src/tools/clippy/tests/ui/checked_conversions.stderr +++ b/src/tools/clippy/tests/ui/checked_conversions.stderr @@ -1,5 +1,5 @@ error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:14:13 + --> tests/ui/checked_conversions.rs:15:13 | LL | let _ = value <= (u32::max_value() as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` @@ -8,97 +8,97 @@ LL | let _ = value <= (u32::max_value() as i64) && value >= 0; = help: to override `-D warnings` add `#[allow(clippy::checked_conversions)]` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:15:13 + --> tests/ui/checked_conversions.rs:16:13 | LL | let _ = value <= (u32::MAX as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:19:13 + --> tests/ui/checked_conversions.rs:20:13 | LL | let _ = value <= i64::from(u16::max_value()) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:20:13 + --> tests/ui/checked_conversions.rs:21:13 | LL | let _ = value <= i64::from(u16::MAX) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:24:13 + --> tests/ui/checked_conversions.rs:25:13 | LL | let _ = value <= (u8::max_value() as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:25:13 + --> tests/ui/checked_conversions.rs:26:13 | LL | let _ = value <= (u8::MAX as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:31:13 + --> tests/ui/checked_conversions.rs:32:13 | LL | let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:32:13 + --> tests/ui/checked_conversions.rs:33:13 | LL | let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:36:13 + --> tests/ui/checked_conversions.rs:37:13 | LL | let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:37:13 + --> tests/ui/checked_conversions.rs:38:13 | LL | let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:43:13 + --> tests/ui/checked_conversions.rs:44:13 | LL | let _ = value <= i32::max_value() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:44:13 + --> tests/ui/checked_conversions.rs:45:13 | LL | let _ = value <= i32::MAX as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:48:13 + --> tests/ui/checked_conversions.rs:49:13 | LL | let _ = value <= isize::max_value() as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:49:13 + --> tests/ui/checked_conversions.rs:50:13 | LL | let _ = value <= isize::MAX as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:53:13 + --> tests/ui/checked_conversions.rs:54:13 | LL | let _ = value <= u16::max_value() as u32 && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:54:13 + --> tests/ui/checked_conversions.rs:55:13 | LL | let _ = value <= u16::MAX as u32 && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:87:13 + --> tests/ui/checked_conversions.rs:88:13 | LL | let _ = value <= (u32::MAX as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` diff --git a/src/tools/clippy/tests/ui/crashes/ice-12616.fixed b/src/tools/clippy/tests/ui/crashes/ice-12616.fixed new file mode 100644 index 0000000000000..a5a5b3d1e78ed --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-12616.fixed @@ -0,0 +1,7 @@ +#![warn(clippy::ptr_as_ptr)] +#![allow(clippy::unnecessary_operation, clippy::unnecessary_cast)] + +fn main() { + let s = std::ptr::null::<()>; + s().cast::<()>(); +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-12616.rs b/src/tools/clippy/tests/ui/crashes/ice-12616.rs new file mode 100644 index 0000000000000..6ee9a5ec08fe9 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-12616.rs @@ -0,0 +1,7 @@ +#![warn(clippy::ptr_as_ptr)] +#![allow(clippy::unnecessary_operation, clippy::unnecessary_cast)] + +fn main() { + let s = std::ptr::null::<()>; + s() as *const (); +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-12616.stderr b/src/tools/clippy/tests/ui/crashes/ice-12616.stderr new file mode 100644 index 0000000000000..ef573f55cf36f --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-12616.stderr @@ -0,0 +1,19 @@ +error: `as` casting between raw pointers without changing its mutability + --> tests/ui/crashes/ice-12616.rs:6:5 + | +LL | s() as *const (); + | ^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `s().cast::<()>()` + | + = note: `-D clippy::ptr-as-ptr` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::ptr_as_ptr)]` + +error: `as` casting between raw pointers without changing its mutability + --> tests/ui/crashes/ice-12616.rs:6:5 + | +LL | s() as *const (); + | ^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `s().cast::<()>()` + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/crashes/ice-1782.rs b/src/tools/clippy/tests/ui/crashes/ice-1782.rs index 19ab03418eed3..73de5721cbc1c 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-1782.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-1782.rs @@ -1,5 +1,5 @@ #![allow(dead_code, unused_variables)] -#![allow(clippy::unnecessary_cast)] +#![allow(clippy::unnecessary_cast, clippy::missing_transmute_annotations)] /// Should not trigger an ICE in `SpanlessEq` / `consts::constant` /// diff --git a/src/tools/clippy/tests/ui/duplicated_attributes.rs b/src/tools/clippy/tests/ui/duplicated_attributes.rs index 0f036c684c1ca..d051c881f15b2 100644 --- a/src/tools/clippy/tests/ui/duplicated_attributes.rs +++ b/src/tools/clippy/tests/ui/duplicated_attributes.rs @@ -2,16 +2,17 @@ #![cfg(any(unix, windows))] #![allow(dead_code)] #![allow(dead_code)] //~ ERROR: duplicated attribute -#![cfg(any(unix, windows))] -//~^ ERROR: duplicated attribute -//~| ERROR: duplicated attribute +#![cfg(any(unix, windows))] // Should not warn! #[cfg(any(unix, windows, target_os = "linux"))] #[allow(dead_code)] #[allow(dead_code)] //~ ERROR: duplicated attribute -#[cfg(any(unix, windows, target_os = "linux"))] -//~^ ERROR: duplicated attribute -//~| ERROR: duplicated attribute +#[cfg(any(unix, windows, target_os = "linux"))] // Should not warn! fn foo() {} +#[cfg(unix)] +#[cfg(windows)] +#[cfg(unix)] //~ ERROR: duplicated attribute +fn bar() {} + fn main() {} diff --git a/src/tools/clippy/tests/ui/duplicated_attributes.stderr b/src/tools/clippy/tests/ui/duplicated_attributes.stderr index 1c6578dbb43a7..9e26ba990ac16 100644 --- a/src/tools/clippy/tests/ui/duplicated_attributes.stderr +++ b/src/tools/clippy/tests/ui/duplicated_attributes.stderr @@ -18,106 +18,38 @@ LL | #![allow(dead_code)] = help: to override `-D warnings` add `#[allow(clippy::duplicated_attributes)]` error: duplicated attribute - --> tests/ui/duplicated_attributes.rs:5:12 - | -LL | #![cfg(any(unix, windows))] - | ^^^^ - | -note: first defined here - --> tests/ui/duplicated_attributes.rs:2:12 - | -LL | #![cfg(any(unix, windows))] - | ^^^^ -help: remove this attribute - --> tests/ui/duplicated_attributes.rs:5:12 - | -LL | #![cfg(any(unix, windows))] - | ^^^^ - -error: duplicated attribute - --> tests/ui/duplicated_attributes.rs:5:18 - | -LL | #![cfg(any(unix, windows))] - | ^^^^^^^ - | -note: first defined here - --> tests/ui/duplicated_attributes.rs:2:18 - | -LL | #![cfg(any(unix, windows))] - | ^^^^^^^ -help: remove this attribute - --> tests/ui/duplicated_attributes.rs:5:18 - | -LL | #![cfg(any(unix, windows))] - | ^^^^^^^ - -error: duplicated attribute - --> tests/ui/duplicated_attributes.rs:11:9 + --> tests/ui/duplicated_attributes.rs:9:9 | LL | #[allow(dead_code)] | ^^^^^^^^^ | note: first defined here - --> tests/ui/duplicated_attributes.rs:10:9 + --> tests/ui/duplicated_attributes.rs:8:9 | LL | #[allow(dead_code)] | ^^^^^^^^^ help: remove this attribute - --> tests/ui/duplicated_attributes.rs:11:9 + --> tests/ui/duplicated_attributes.rs:9:9 | LL | #[allow(dead_code)] | ^^^^^^^^^ error: duplicated attribute - --> tests/ui/duplicated_attributes.rs:12:11 - | -LL | #[cfg(any(unix, windows, target_os = "linux"))] - | ^^^^ - | -note: first defined here - --> tests/ui/duplicated_attributes.rs:9:11 - | -LL | #[cfg(any(unix, windows, target_os = "linux"))] - | ^^^^ -help: remove this attribute - --> tests/ui/duplicated_attributes.rs:12:11 - | -LL | #[cfg(any(unix, windows, target_os = "linux"))] - | ^^^^ - -error: duplicated attribute - --> tests/ui/duplicated_attributes.rs:12:17 - | -LL | #[cfg(any(unix, windows, target_os = "linux"))] - | ^^^^^^^ - | -note: first defined here - --> tests/ui/duplicated_attributes.rs:9:17 - | -LL | #[cfg(any(unix, windows, target_os = "linux"))] - | ^^^^^^^ -help: remove this attribute - --> tests/ui/duplicated_attributes.rs:12:17 - | -LL | #[cfg(any(unix, windows, target_os = "linux"))] - | ^^^^^^^ - -error: duplicated attribute - --> tests/ui/duplicated_attributes.rs:12:26 + --> tests/ui/duplicated_attributes.rs:15:7 | -LL | #[cfg(any(unix, windows, target_os = "linux"))] - | ^^^^^^^^^^^^^^^^^^^ +LL | #[cfg(unix)] + | ^^^^ | note: first defined here - --> tests/ui/duplicated_attributes.rs:9:26 + --> tests/ui/duplicated_attributes.rs:13:7 | -LL | #[cfg(any(unix, windows, target_os = "linux"))] - | ^^^^^^^^^^^^^^^^^^^ +LL | #[cfg(unix)] + | ^^^^ help: remove this attribute - --> tests/ui/duplicated_attributes.rs:12:26 + --> tests/ui/duplicated_attributes.rs:15:7 | -LL | #[cfg(any(unix, windows, target_os = "linux"))] - | ^^^^^^^^^^^^^^^^^^^ +LL | #[cfg(unix)] + | ^^^^ -error: aborting due to 7 previous errors +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/eager_transmute.fixed b/src/tools/clippy/tests/ui/eager_transmute.fixed index bece09bba1abd..c29e7dd9ab3ea 100644 --- a/src/tools/clippy/tests/ui/eager_transmute.fixed +++ b/src/tools/clippy/tests/ui/eager_transmute.fixed @@ -1,6 +1,6 @@ #![feature(rustc_attrs)] #![warn(clippy::eager_transmute)] -#![allow(clippy::transmute_int_to_non_zero)] +#![allow(clippy::transmute_int_to_non_zero, clippy::missing_transmute_annotations)] use std::num::NonZeroU8; diff --git a/src/tools/clippy/tests/ui/eager_transmute.rs b/src/tools/clippy/tests/ui/eager_transmute.rs index a82bd578f76cc..491a9485c9327 100644 --- a/src/tools/clippy/tests/ui/eager_transmute.rs +++ b/src/tools/clippy/tests/ui/eager_transmute.rs @@ -1,6 +1,6 @@ #![feature(rustc_attrs)] #![warn(clippy::eager_transmute)] -#![allow(clippy::transmute_int_to_non_zero)] +#![allow(clippy::transmute_int_to_non_zero, clippy::missing_transmute_annotations)] use std::num::NonZeroU8; diff --git a/src/tools/clippy/tests/ui/filter_map_identity.fixed b/src/tools/clippy/tests/ui/filter_map_identity.fixed index ad438afaca779..f3f6848e5f92f 100644 --- a/src/tools/clippy/tests/ui/filter_map_identity.fixed +++ b/src/tools/clippy/tests/ui/filter_map_identity.fixed @@ -1,17 +1,83 @@ -#![allow(unused_imports, clippy::needless_return)] +#![allow(unused_imports, clippy::needless_return, clippy::useless_vec)] #![warn(clippy::filter_map_identity)] +#![feature(stmt_expr_attributes)] + +use std::option::Option; +struct NonCopy; +use std::convert::identity; + +fn non_copy_vec() -> Vec> { + todo!() +} + +fn copy_vec() -> Vec> { + todo!() +} + +fn copy_vec_non_inferred() -> Vec> { + todo!() +} + +fn opaque() -> impl IntoIterator> { + vec![Some(T::default())] +} fn main() { - let iterator = vec![Some(1), None, Some(2)].into_iter(); - let _ = iterator.flatten(); + { + // into_iter + copy_vec_non_inferred().into_iter().flatten(); + //~^ ERROR: use of + copy_vec_non_inferred().into_iter().flatten(); + //~^ ERROR: use of + copy_vec_non_inferred().into_iter().flatten(); + //~^ ERROR: use of + copy_vec_non_inferred().into_iter().flatten(); + //~^ ERROR: use of + copy_vec_non_inferred().into_iter().flatten(); + //~^ ERROR: use of + + non_copy_vec().into_iter().flatten(); + //~^ ERROR: use of + non_copy_vec().into_iter().flatten(); + //~^ ERROR: use of + + non_copy_vec().into_iter().flatten(); + //~^ ERROR: use of + non_copy_vec().into_iter().flatten(); + //~^ ERROR: use of + non_copy_vec().into_iter().flatten(); + //~^ ERROR: use of + non_copy_vec().into_iter().flatten(); + //~^ ERROR: use of - let iterator = vec![Some(1), None, Some(2)].into_iter(); - let _ = iterator.flatten(); + copy_vec::().into_iter().flatten(); + //~^ ERROR: use of + copy_vec::().into_iter().flatten(); + //~^ ERROR: use of + copy_vec::().into_iter().flatten(); + //~^ ERROR: use of + copy_vec::().into_iter().flatten(); + //~^ ERROR: use of - use std::convert::identity; - let iterator = vec![Some(1), None, Some(2)].into_iter(); - let _ = iterator.flatten(); + // we are forced to pass the type in the call. + copy_vec::().into_iter().flatten(); + //~^ ERROR: use of + copy_vec::().into_iter().flatten(); + //~^ ERROR: use of + copy_vec::().into_iter().flatten(); + //~^ ERROR: use of + copy_vec::().into_iter().flatten(); + //~^ ERROR: use of + #[rustfmt::skip] + copy_vec::().into_iter().flatten(); + //~^ ERROR: use of + #[rustfmt::skip] + copy_vec::().into_iter().flatten(); + //~^ ERROR: use of - let iterator = vec![Some(1), None, Some(2)].into_iter(); - let _ = iterator.flatten(); + // note, the compiler requires that we pass the type to `opaque`. This is mostly for reference, + // it behaves the same as copy_vec. + opaque::().into_iter().flatten(); + //~^ ERROR: use of + } } diff --git a/src/tools/clippy/tests/ui/filter_map_identity.rs b/src/tools/clippy/tests/ui/filter_map_identity.rs index d7423276872ac..b9aa9c05be89c 100644 --- a/src/tools/clippy/tests/ui/filter_map_identity.rs +++ b/src/tools/clippy/tests/ui/filter_map_identity.rs @@ -1,17 +1,83 @@ -#![allow(unused_imports, clippy::needless_return)] +#![allow(unused_imports, clippy::needless_return, clippy::useless_vec)] #![warn(clippy::filter_map_identity)] +#![feature(stmt_expr_attributes)] + +use std::option::Option; +struct NonCopy; +use std::convert::identity; + +fn non_copy_vec() -> Vec> { + todo!() +} + +fn copy_vec() -> Vec> { + todo!() +} + +fn copy_vec_non_inferred() -> Vec> { + todo!() +} + +fn opaque() -> impl IntoIterator> { + vec![Some(T::default())] +} fn main() { - let iterator = vec![Some(1), None, Some(2)].into_iter(); - let _ = iterator.filter_map(|x| x); + { + // into_iter + copy_vec_non_inferred().into_iter().filter_map(|x| x); + //~^ ERROR: use of + copy_vec_non_inferred().into_iter().filter_map(std::convert::identity); + //~^ ERROR: use of + copy_vec_non_inferred().into_iter().filter_map(identity); + //~^ ERROR: use of + copy_vec_non_inferred().into_iter().filter_map(|x| return x); + //~^ ERROR: use of + copy_vec_non_inferred().into_iter().filter_map(|x| return x); + //~^ ERROR: use of + + non_copy_vec().into_iter().filter_map(|x| x); + //~^ ERROR: use of + non_copy_vec().into_iter().filter_map(|x| x); + //~^ ERROR: use of + + non_copy_vec().into_iter().filter_map(std::convert::identity); + //~^ ERROR: use of + non_copy_vec().into_iter().filter_map(identity); + //~^ ERROR: use of + non_copy_vec().into_iter().filter_map(|x| return x); + //~^ ERROR: use of + non_copy_vec().into_iter().filter_map(|x| return x); + //~^ ERROR: use of - let iterator = vec![Some(1), None, Some(2)].into_iter(); - let _ = iterator.filter_map(std::convert::identity); + copy_vec::().into_iter().filter_map(|x: Option<_>| x); + //~^ ERROR: use of + copy_vec::().into_iter().filter_map(|x: Option<_>| x); + //~^ ERROR: use of + copy_vec::().into_iter().filter_map(|x: Option<_>| return x); + //~^ ERROR: use of + copy_vec::().into_iter().filter_map(|x: Option<_>| return x); + //~^ ERROR: use of - use std::convert::identity; - let iterator = vec![Some(1), None, Some(2)].into_iter(); - let _ = iterator.filter_map(identity); + // we are forced to pass the type in the call. + copy_vec::().into_iter().filter_map(|x: Option| x); + //~^ ERROR: use of + copy_vec::().into_iter().filter_map(|x: Option| x); + //~^ ERROR: use of + copy_vec::().into_iter().filter_map(|x: Option| return x); + //~^ ERROR: use of + copy_vec::().into_iter().filter_map(|x: Option| return x); + //~^ ERROR: use of + #[rustfmt::skip] + copy_vec::().into_iter().filter_map(|x: Option| -> Option {{ x }}); + //~^ ERROR: use of + #[rustfmt::skip] + copy_vec::().into_iter().filter_map(|x: Option| -> Option {{ return x }}); + //~^ ERROR: use of - let iterator = vec![Some(1), None, Some(2)].into_iter(); - let _ = iterator.filter_map(|x| return x); + // note, the compiler requires that we pass the type to `opaque`. This is mostly for reference, + // it behaves the same as copy_vec. + opaque::().into_iter().filter_map(|x| x); + //~^ ERROR: use of + } } diff --git a/src/tools/clippy/tests/ui/filter_map_identity.stderr b/src/tools/clippy/tests/ui/filter_map_identity.stderr index 5aa46ad6d2375..55068db4e9d03 100644 --- a/src/tools/clippy/tests/ui/filter_map_identity.stderr +++ b/src/tools/clippy/tests/ui/filter_map_identity.stderr @@ -1,29 +1,137 @@ error: use of `filter_map` with an identity function - --> tests/ui/filter_map_identity.rs:6:22 + --> tests/ui/filter_map_identity.rs:28:45 | -LL | let _ = iterator.filter_map(|x| x); - | ^^^^^^^^^^^^^^^^^ help: try: `flatten()` +LL | copy_vec_non_inferred().into_iter().filter_map(|x| x); + | ^^^^^^^^^^^^^^^^^ help: try: `flatten()` | = note: `-D clippy::filter-map-identity` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::filter_map_identity)]` error: use of `filter_map` with an identity function - --> tests/ui/filter_map_identity.rs:9:22 + --> tests/ui/filter_map_identity.rs:30:45 | -LL | let _ = iterator.filter_map(std::convert::identity); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` +LL | copy_vec_non_inferred().into_iter().filter_map(std::convert::identity); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` error: use of `filter_map` with an identity function - --> tests/ui/filter_map_identity.rs:13:22 + --> tests/ui/filter_map_identity.rs:32:45 | -LL | let _ = iterator.filter_map(identity); - | ^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` +LL | copy_vec_non_inferred().into_iter().filter_map(identity); + | ^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` error: use of `filter_map` with an identity function - --> tests/ui/filter_map_identity.rs:16:22 + --> tests/ui/filter_map_identity.rs:34:45 | -LL | let _ = iterator.filter_map(|x| return x); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` +LL | copy_vec_non_inferred().into_iter().filter_map(|x| return x); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` -error: aborting due to 4 previous errors +error: use of `filter_map` with an identity function + --> tests/ui/filter_map_identity.rs:36:45 + | +LL | copy_vec_non_inferred().into_iter().filter_map(|x| return x); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` + +error: use of `filter_map` with an identity function + --> tests/ui/filter_map_identity.rs:39:36 + | +LL | non_copy_vec().into_iter().filter_map(|x| x); + | ^^^^^^^^^^^^^^^^^ help: try: `flatten()` + +error: use of `filter_map` with an identity function + --> tests/ui/filter_map_identity.rs:41:36 + | +LL | non_copy_vec().into_iter().filter_map(|x| x); + | ^^^^^^^^^^^^^^^^^ help: try: `flatten()` + +error: use of `filter_map` with an identity function + --> tests/ui/filter_map_identity.rs:44:36 + | +LL | non_copy_vec().into_iter().filter_map(std::convert::identity); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` + +error: use of `filter_map` with an identity function + --> tests/ui/filter_map_identity.rs:46:36 + | +LL | non_copy_vec().into_iter().filter_map(identity); + | ^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` + +error: use of `filter_map` with an identity function + --> tests/ui/filter_map_identity.rs:48:36 + | +LL | non_copy_vec().into_iter().filter_map(|x| return x); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` + +error: use of `filter_map` with an identity function + --> tests/ui/filter_map_identity.rs:50:36 + | +LL | non_copy_vec().into_iter().filter_map(|x| return x); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` + +error: use of `filter_map` with an identity function + --> tests/ui/filter_map_identity.rs:53:39 + | +LL | copy_vec::().into_iter().filter_map(|x: Option<_>| x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` + +error: use of `filter_map` with an identity function + --> tests/ui/filter_map_identity.rs:55:39 + | +LL | copy_vec::().into_iter().filter_map(|x: Option<_>| x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` + +error: use of `filter_map` with an identity function + --> tests/ui/filter_map_identity.rs:57:39 + | +LL | copy_vec::().into_iter().filter_map(|x: Option<_>| return x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` + +error: use of `filter_map` with an identity function + --> tests/ui/filter_map_identity.rs:59:39 + | +LL | copy_vec::().into_iter().filter_map(|x: Option<_>| return x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` + +error: use of `filter_map` with an identity function + --> tests/ui/filter_map_identity.rs:63:39 + | +LL | copy_vec::().into_iter().filter_map(|x: Option| x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` + +error: use of `filter_map` with an identity function + --> tests/ui/filter_map_identity.rs:65:39 + | +LL | copy_vec::().into_iter().filter_map(|x: Option| x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` + +error: use of `filter_map` with an identity function + --> tests/ui/filter_map_identity.rs:67:39 + | +LL | copy_vec::().into_iter().filter_map(|x: Option| return x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` + +error: use of `filter_map` with an identity function + --> tests/ui/filter_map_identity.rs:69:39 + | +LL | copy_vec::().into_iter().filter_map(|x: Option| return x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` + +error: use of `filter_map` with an identity function + --> tests/ui/filter_map_identity.rs:72:43 + | +LL | copy_vec::().into_iter().filter_map(|x: Option| -> Option {{ x }}); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` + +error: use of `filter_map` with an identity function + --> tests/ui/filter_map_identity.rs:75:43 + | +LL | copy_vec::().into_iter().filter_map(|x: Option| -> Option {{ return x }}); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` + +error: use of `filter_map` with an identity function + --> tests/ui/filter_map_identity.rs:80:37 + | +LL | opaque::().into_iter().filter_map(|x| x); + | ^^^^^^^^^^^^^^^^^ help: try: `flatten()` + +error: aborting due to 22 previous errors diff --git a/src/tools/clippy/tests/ui/large_stack_frames.rs b/src/tools/clippy/tests/ui/large_stack_frames.rs index f32368f93975c..e6c030b8a9e80 100644 --- a/src/tools/clippy/tests/ui/large_stack_frames.rs +++ b/src/tools/clippy/tests/ui/large_stack_frames.rs @@ -1,3 +1,5 @@ +//@ normalize-stderr-test: "\b10000(08|16|32)\b" -> "100$$PTR" +//@ normalize-stderr-test: "\b2500(060|120)\b" -> "250$$PTR" #![allow(unused, incomplete_features)] #![warn(clippy::large_stack_frames)] #![feature(unsized_locals)] @@ -23,8 +25,7 @@ impl Default for ArrayDefault { } fn many_small_arrays() { - //~^ ERROR: this function allocates a large amount of stack space - //~| NOTE: allocating large amounts of stack space can overflow the stack + //~^ ERROR: this function may allocate let x = [0u8; 500_000]; let x2 = [0u8; 500_000]; let x3 = [0u8; 500_000]; @@ -34,17 +35,21 @@ fn many_small_arrays() { } fn large_return_value() -> ArrayDefault<1_000_000> { - //~^ ERROR: this function allocates a large amount of stack space - //~| NOTE: allocating large amounts of stack space can overflow the stack + //~^ ERROR: this function may allocate 1000000 bytes on the stack Default::default() } fn large_fn_arg(x: ArrayDefault<1_000_000>) { - //~^ ERROR: this function allocates a large amount of stack space - //~| NOTE: allocating large amounts of stack space can overflow the stack + //~^ ERROR: this function may allocate black_box(&x); } +fn has_large_closure() { + let f = || black_box(&[0u8; 1_000_000]); + //~^ ERROR: this function may allocate + f(); +} + fn main() { generic::>(); } diff --git a/src/tools/clippy/tests/ui/large_stack_frames.stderr b/src/tools/clippy/tests/ui/large_stack_frames.stderr index b99500fd9c34d..f2e0a127f5f50 100644 --- a/src/tools/clippy/tests/ui/large_stack_frames.stderr +++ b/src/tools/clippy/tests/ui/large_stack_frames.stderr @@ -1,42 +1,42 @@ -error: this function allocates a large amount of stack space - --> tests/ui/large_stack_frames.rs:25:1 - | -LL | / fn many_small_arrays() { -LL | | -LL | | -LL | | let x = [0u8; 500_000]; -... | -LL | | black_box((&x, &x2, &x3, &x4, &x5)); -LL | | } - | |_^ - | - = note: allocating large amounts of stack space can overflow the stack +error: this function may allocate 250$PTR bytes on the stack + --> tests/ui/large_stack_frames.rs:27:4 + | +LL | fn many_small_arrays() { + | ^^^^^^^^^^^^^^^^^ +... +LL | let x5 = [0u8; 500_000]; + | -- `x5` is the largest part, at 500000 bytes for type `[u8; 500000]` + | + = note: 250$PTR bytes is larger than Clippy's configured `stack-size-threshold` of 512000 + = note: allocating large amounts of stack space can overflow the stack and cause the program to abort = note: `-D clippy::large-stack-frames` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::large_stack_frames)]` -error: this function allocates a large amount of stack space - --> tests/ui/large_stack_frames.rs:36:1 +error: this function may allocate 1000000 bytes on the stack + --> tests/ui/large_stack_frames.rs:37:4 + | +LL | fn large_return_value() -> ArrayDefault<1_000_000> { + | ^^^^^^^^^^^^^^^^^^ ----------------------- this is the largest part, at 1000000 bytes for type `ArrayDefault<1000000>` + | + = note: 1000000 bytes is larger than Clippy's configured `stack-size-threshold` of 512000 + +error: this function may allocate 100$PTR bytes on the stack + --> tests/ui/large_stack_frames.rs:42:4 | -LL | / fn large_return_value() -> ArrayDefault<1_000_000> { -LL | | -LL | | -LL | | Default::default() -LL | | } - | |_^ +LL | fn large_fn_arg(x: ArrayDefault<1_000_000>) { + | ^^^^^^^^^^^^ - `x` is the largest part, at 1000000 bytes for type `ArrayDefault<1000000>` | - = note: allocating large amounts of stack space can overflow the stack + = note: 100$PTR bytes is larger than Clippy's configured `stack-size-threshold` of 512000 -error: this function allocates a large amount of stack space - --> tests/ui/large_stack_frames.rs:42:1 +error: this function may allocate 100$PTR bytes on the stack + --> tests/ui/large_stack_frames.rs:48:13 | -LL | / fn large_fn_arg(x: ArrayDefault<1_000_000>) { -LL | | -LL | | -LL | | black_box(&x); -LL | | } - | |_^ +LL | let f = || black_box(&[0u8; 1_000_000]); + | ^^^^^^^^^^^^^^----------------^ + | | + | this is the largest part, at 1000000 bytes for type `[u8; 1000000]` | - = note: allocating large amounts of stack space can overflow the stack + = note: 100$PTR bytes is larger than Clippy's configured `stack-size-threshold` of 512000 -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/legacy_numeric_constants.fixed b/src/tools/clippy/tests/ui/legacy_numeric_constants.fixed new file mode 100644 index 0000000000000..a6ef8f8c119a5 --- /dev/null +++ b/src/tools/clippy/tests/ui/legacy_numeric_constants.fixed @@ -0,0 +1,117 @@ +//@aux-build:proc_macros.rs +#![allow(clippy::no_effect, deprecated, unused)] +#![allow(clippy::legacy_numeric_constants)] // For imports. + +#[macro_use] +extern crate proc_macros; + +pub mod a { + pub use std::u128; +} + +macro_rules! b { + () => { + mod b { + #[warn(clippy::legacy_numeric_constants)] + fn b() { + let x = u64::MAX; + //~^ ERROR: usage of a legacy numeric constant + //~| HELP: use the associated constant instead + } + } + }; +} + +use std::u32::MAX; +use std::u8::MIN; +use std::{f64, u32}; + +#[warn(clippy::legacy_numeric_constants)] +fn main() { + f32::EPSILON; + //~^ ERROR: usage of a legacy numeric constant + //~| HELP: use the associated constant instead + u8::MIN; + //~^ ERROR: usage of a legacy numeric constant + //~| HELP: use the associated constant instead + usize::MIN; + //~^ ERROR: usage of a legacy numeric constant + //~| HELP: use the associated constant instead + u32::MAX; + //~^ ERROR: usage of a legacy numeric constant + //~| HELP: use the associated constant instead + u32::MAX; + //~^ ERROR: usage of a legacy numeric constant + //~| HELP: use the associated constant instead + u32::MAX; + //~^ ERROR: usage of a legacy numeric constant + //~| HELP: use the associated constant instead + i32::MAX; + //~^ ERROR: usage of a legacy numeric method + //~| HELP: use the associated constant instead + u8::MAX; + //~^ ERROR: usage of a legacy numeric method + //~| HELP: use the associated constant instead + u8::MIN; + //~^ ERROR: usage of a legacy numeric method + //~| HELP: use the associated constant instead + u8::MIN; + //~^ ERROR: usage of a legacy numeric constant + //~| HELP: use the associated constant instead + ::std::primitive::u8::MIN; + //~^ ERROR: usage of a legacy numeric method + //~| HELP: use the associated constant instead + std::primitive::i32::MAX; + //~^ ERROR: usage of a legacy numeric method + //~| HELP: use the associated constant instead + u128::MAX; + //~^ ERROR: usage of a legacy numeric constant + //~| HELP: use the associated constant instead + u32::MAX; + u128::MAX; + f32::EPSILON; + ::std::primitive::u8::MIN; + std::f32::consts::E; + f64::consts::E; + u8::MIN; + std::f32::consts::E; + f64::consts::E; + b!(); + + [(0, "", i128::MAX)]; + //~^ ERROR: usage of a legacy numeric constant + //~| HELP: use the associated constant instead +} + +#[warn(clippy::legacy_numeric_constants)] +fn ext() { + external! { + ::std::primitive::u8::MIN; + ::std::u8::MIN; + ::std::primitive::u8::min_value(); + use std::u64; + use std::u8::MIN; + } +} + +#[allow(clippy::legacy_numeric_constants)] +fn allow() { + ::std::primitive::u8::MIN; + ::std::u8::MIN; + ::std::primitive::u8::min_value(); + use std::u64; + use std::u8::MIN; +} + +#[warn(clippy::legacy_numeric_constants)] +#[clippy::msrv = "1.42.0"] +fn msrv_too_low() { + std::u32::MAX; +} + +#[warn(clippy::legacy_numeric_constants)] +#[clippy::msrv = "1.43.0"] +fn msrv_juust_right() { + u32::MAX; + //~^ ERROR: usage of a legacy numeric constant +} diff --git a/src/tools/clippy/tests/ui/legacy_numeric_constants.rs b/src/tools/clippy/tests/ui/legacy_numeric_constants.rs new file mode 100644 index 0000000000000..cd633545372cd --- /dev/null +++ b/src/tools/clippy/tests/ui/legacy_numeric_constants.rs @@ -0,0 +1,117 @@ +//@aux-build:proc_macros.rs +#![allow(clippy::no_effect, deprecated, unused)] +#![allow(clippy::legacy_numeric_constants)] // For imports. + +#[macro_use] +extern crate proc_macros; + +pub mod a { + pub use std::u128; +} + +macro_rules! b { + () => { + mod b { + #[warn(clippy::legacy_numeric_constants)] + fn b() { + let x = std::u64::MAX; + //~^ ERROR: usage of a legacy numeric constant + //~| HELP: use the associated constant instead + } + } + }; +} + +use std::u32::MAX; +use std::u8::MIN; +use std::{f64, u32}; + +#[warn(clippy::legacy_numeric_constants)] +fn main() { + std::f32::EPSILON; + //~^ ERROR: usage of a legacy numeric constant + //~| HELP: use the associated constant instead + std::u8::MIN; + //~^ ERROR: usage of a legacy numeric constant + //~| HELP: use the associated constant instead + std::usize::MIN; + //~^ ERROR: usage of a legacy numeric constant + //~| HELP: use the associated constant instead + std::u32::MAX; + //~^ ERROR: usage of a legacy numeric constant + //~| HELP: use the associated constant instead + core::u32::MAX; + //~^ ERROR: usage of a legacy numeric constant + //~| HELP: use the associated constant instead + MAX; + //~^ ERROR: usage of a legacy numeric constant + //~| HELP: use the associated constant instead + i32::max_value(); + //~^ ERROR: usage of a legacy numeric method + //~| HELP: use the associated constant instead + u8::max_value(); + //~^ ERROR: usage of a legacy numeric method + //~| HELP: use the associated constant instead + u8::min_value(); + //~^ ERROR: usage of a legacy numeric method + //~| HELP: use the associated constant instead + ::std::u8::MIN; + //~^ ERROR: usage of a legacy numeric constant + //~| HELP: use the associated constant instead + ::std::primitive::u8::min_value(); + //~^ ERROR: usage of a legacy numeric method + //~| HELP: use the associated constant instead + std::primitive::i32::max_value(); + //~^ ERROR: usage of a legacy numeric method + //~| HELP: use the associated constant instead + self::a::u128::MAX; + //~^ ERROR: usage of a legacy numeric constant + //~| HELP: use the associated constant instead + u32::MAX; + u128::MAX; + f32::EPSILON; + ::std::primitive::u8::MIN; + std::f32::consts::E; + f64::consts::E; + u8::MIN; + std::f32::consts::E; + f64::consts::E; + b!(); + + [(0, "", std::i128::MAX)]; + //~^ ERROR: usage of a legacy numeric constant + //~| HELP: use the associated constant instead +} + +#[warn(clippy::legacy_numeric_constants)] +fn ext() { + external! { + ::std::primitive::u8::MIN; + ::std::u8::MIN; + ::std::primitive::u8::min_value(); + use std::u64; + use std::u8::MIN; + } +} + +#[allow(clippy::legacy_numeric_constants)] +fn allow() { + ::std::primitive::u8::MIN; + ::std::u8::MIN; + ::std::primitive::u8::min_value(); + use std::u64; + use std::u8::MIN; +} + +#[warn(clippy::legacy_numeric_constants)] +#[clippy::msrv = "1.42.0"] +fn msrv_too_low() { + std::u32::MAX; +} + +#[warn(clippy::legacy_numeric_constants)] +#[clippy::msrv = "1.43.0"] +fn msrv_juust_right() { + std::u32::MAX; + //~^ ERROR: usage of a legacy numeric constant +} diff --git a/src/tools/clippy/tests/ui/legacy_numeric_constants.stderr b/src/tools/clippy/tests/ui/legacy_numeric_constants.stderr new file mode 100644 index 0000000000000..267b9ac8e4d11 --- /dev/null +++ b/src/tools/clippy/tests/ui/legacy_numeric_constants.stderr @@ -0,0 +1,184 @@ +error: usage of a legacy numeric constant + --> tests/ui/legacy_numeric_constants.rs:31:5 + | +LL | std::f32::EPSILON; + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::legacy-numeric-constants` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::legacy_numeric_constants)]` +help: use the associated constant instead + | +LL | f32::EPSILON; + | ~~~~~~~~~~~~ + +error: usage of a legacy numeric constant + --> tests/ui/legacy_numeric_constants.rs:34:5 + | +LL | std::u8::MIN; + | ^^^^^^^^^^^^ + | +help: use the associated constant instead + | +LL | u8::MIN; + | ~~~~~~~ + +error: usage of a legacy numeric constant + --> tests/ui/legacy_numeric_constants.rs:37:5 + | +LL | std::usize::MIN; + | ^^^^^^^^^^^^^^^ + | +help: use the associated constant instead + | +LL | usize::MIN; + | ~~~~~~~~~~ + +error: usage of a legacy numeric constant + --> tests/ui/legacy_numeric_constants.rs:40:5 + | +LL | std::u32::MAX; + | ^^^^^^^^^^^^^ + | +help: use the associated constant instead + | +LL | u32::MAX; + | ~~~~~~~~ + +error: usage of a legacy numeric constant + --> tests/ui/legacy_numeric_constants.rs:43:5 + | +LL | core::u32::MAX; + | ^^^^^^^^^^^^^^ + | +help: use the associated constant instead + | +LL | u32::MAX; + | ~~~~~~~~ + +error: usage of a legacy numeric constant + --> tests/ui/legacy_numeric_constants.rs:46:5 + | +LL | MAX; + | ^^^ + | +help: use the associated constant instead + | +LL | u32::MAX; + | ~~~~~~~~ + +error: usage of a legacy numeric method + --> tests/ui/legacy_numeric_constants.rs:49:10 + | +LL | i32::max_value(); + | ^^^^^^^^^^^ + | +help: use the associated constant instead + | +LL | i32::MAX; + | ~~~ + +error: usage of a legacy numeric method + --> tests/ui/legacy_numeric_constants.rs:52:9 + | +LL | u8::max_value(); + | ^^^^^^^^^^^ + | +help: use the associated constant instead + | +LL | u8::MAX; + | ~~~ + +error: usage of a legacy numeric method + --> tests/ui/legacy_numeric_constants.rs:55:9 + | +LL | u8::min_value(); + | ^^^^^^^^^^^ + | +help: use the associated constant instead + | +LL | u8::MIN; + | ~~~ + +error: usage of a legacy numeric constant + --> tests/ui/legacy_numeric_constants.rs:58:5 + | +LL | ::std::u8::MIN; + | ^^^^^^^^^^^^^^ + | +help: use the associated constant instead + | +LL | u8::MIN; + | ~~~~~~~ + +error: usage of a legacy numeric method + --> tests/ui/legacy_numeric_constants.rs:61:27 + | +LL | ::std::primitive::u8::min_value(); + | ^^^^^^^^^^^ + | +help: use the associated constant instead + | +LL | ::std::primitive::u8::MIN; + | ~~~ + +error: usage of a legacy numeric method + --> tests/ui/legacy_numeric_constants.rs:64:26 + | +LL | std::primitive::i32::max_value(); + | ^^^^^^^^^^^ + | +help: use the associated constant instead + | +LL | std::primitive::i32::MAX; + | ~~~ + +error: usage of a legacy numeric constant + --> tests/ui/legacy_numeric_constants.rs:67:5 + | +LL | self::a::u128::MAX; + | ^^^^^^^^^^^^^^^^^^ + | +help: use the associated constant instead + | +LL | u128::MAX; + | ~~~~~~~~~ + +error: usage of a legacy numeric constant + --> tests/ui/legacy_numeric_constants.rs:17:25 + | +LL | let x = std::u64::MAX; + | ^^^^^^^^^^^^^ +... +LL | b!(); + | ---- in this macro invocation + | + = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) +help: use the associated constant instead + | +LL | let x = u64::MAX; + | ~~~~~~~~ + +error: usage of a legacy numeric constant + --> tests/ui/legacy_numeric_constants.rs:81:14 + | +LL | [(0, "", std::i128::MAX)]; + | ^^^^^^^^^^^^^^ + | +help: use the associated constant instead + | +LL | [(0, "", i128::MAX)]; + | ~~~~~~~~~ + +error: usage of a legacy numeric constant + --> tests/ui/legacy_numeric_constants.rs:115:5 + | +LL | std::u32::MAX; + | ^^^^^^^^^^^^^ + | +help: use the associated constant instead + | +LL | u32::MAX; + | ~~~~~~~~ + +error: aborting due to 16 previous errors + diff --git a/src/tools/clippy/tests/ui/legacy_numeric_constants_unfixable.rs b/src/tools/clippy/tests/ui/legacy_numeric_constants_unfixable.rs new file mode 100644 index 0000000000000..86738ede210cb --- /dev/null +++ b/src/tools/clippy/tests/ui/legacy_numeric_constants_unfixable.rs @@ -0,0 +1,78 @@ +//@no-rustfix +//@aux-build:proc_macros.rs +#![allow(clippy::no_effect, deprecated, unused)] +#![warn(clippy::legacy_numeric_constants)] + +#[macro_use] +extern crate proc_macros; + +use std::u128 as _; +//~^ ERROR: importing legacy numeric constants +//~| HELP: remove this import +pub mod a { + pub use std::{mem, u128}; + //~^ ERROR: importing legacy numeric constants + //~| HELP: remove this import +} + +macro_rules! b { + () => { + mod b { + use std::u32; + //~^ ERROR: importing legacy numeric constants + //~| HELP: remove this import + } + }; +} + +fn main() { + use std::u32::MAX; + //~^ ERROR: importing a legacy numeric constant + //~| HELP: remove this import and use the associated constant `u32::MAX` + use std::u8::MIN; + //~^ ERROR: importing a legacy numeric constant + //~| HELP: remove this import and use the associated constant `u8::MIN` + f64::MAX; + use std::u32; + //~^ ERROR: importing legacy numeric constants + //~| HELP: remove this import + u32::MAX; + use std::f32::MIN_POSITIVE; + //~^ ERROR: importing a legacy numeric constant + //~| HELP: remove this import and use the associated constant `f32::MIN_POSITIVE` + use std::f64; + use std::i16::*; + //~^ ERROR: importing legacy numeric constants + //~| HELP: remove this import and use associated constants `i16::` + u128::MAX; + f32::EPSILON; + f64::EPSILON; + ::std::primitive::u8::MIN; + std::f32::consts::E; + f64::consts::E; + u8::MIN; + std::f32::consts::E; + f64::consts::E; + b!(); +} + +fn ext() { + external! { + ::std::primitive::u8::MIN; + ::std::u8::MIN; + ::std::primitive::u8::min_value(); + use std::u64; + use std::u8::MIN; + } +} + +#[clippy::msrv = "1.42.0"] +fn msrv_too_low() { + use std::u32::MAX; +} + +#[clippy::msrv = "1.43.0"] +fn msrv_juust_right() { + use std::u32::MAX; + //~^ ERROR: importing a legacy numeric constant +} diff --git a/src/tools/clippy/tests/ui/legacy_numeric_constants_unfixable.stderr b/src/tools/clippy/tests/ui/legacy_numeric_constants_unfixable.stderr new file mode 100644 index 0000000000000..2edcf71883627 --- /dev/null +++ b/src/tools/clippy/tests/ui/legacy_numeric_constants_unfixable.stderr @@ -0,0 +1,83 @@ +error: importing legacy numeric constants + --> tests/ui/legacy_numeric_constants_unfixable.rs:9:5 + | +LL | use std::u128 as _; + | ^^^^^^^^^ + | + = help: remove this import + = note: `-D clippy::legacy-numeric-constants` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::legacy_numeric_constants)]` + +error: importing legacy numeric constants + --> tests/ui/legacy_numeric_constants_unfixable.rs:13:24 + | +LL | pub use std::{mem, u128}; + | ^^^^ + | + = help: remove this import + = note: then `u128::` will resolve to the respective associated constant + +error: importing a legacy numeric constant + --> tests/ui/legacy_numeric_constants_unfixable.rs:29:9 + | +LL | use std::u32::MAX; + | ^^^^^^^^^^^^^ + | + = help: remove this import and use the associated constant `u32::MAX` from the primitive type instead + +error: importing a legacy numeric constant + --> tests/ui/legacy_numeric_constants_unfixable.rs:32:9 + | +LL | use std::u8::MIN; + | ^^^^^^^^^^^^ + | + = help: remove this import and use the associated constant `u8::MIN` from the primitive type instead + +error: importing legacy numeric constants + --> tests/ui/legacy_numeric_constants_unfixable.rs:36:9 + | +LL | use std::u32; + | ^^^^^^^^ + | + = help: remove this import + = note: then `u32::` will resolve to the respective associated constant + +error: importing a legacy numeric constant + --> tests/ui/legacy_numeric_constants_unfixable.rs:40:9 + | +LL | use std::f32::MIN_POSITIVE; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove this import and use the associated constant `f32::MIN_POSITIVE` from the primitive type instead + +error: importing legacy numeric constants + --> tests/ui/legacy_numeric_constants_unfixable.rs:44:9 + | +LL | use std::i16::*; + | ^^^^^^^^ + | + = help: remove this import and use associated constants `i16::` from the primitive type instead + +error: importing legacy numeric constants + --> tests/ui/legacy_numeric_constants_unfixable.rs:21:17 + | +LL | use std::u32; + | ^^^^^^^^ +... +LL | b!(); + | ---- in this macro invocation + | + = help: remove this import + = note: then `u32::` will resolve to the respective associated constant + = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: importing a legacy numeric constant + --> tests/ui/legacy_numeric_constants_unfixable.rs:76:9 + | +LL | use std::u32::MAX; + | ^^^^^^^^^^^^^ + | + = help: remove this import and use the associated constant `u32::MAX` from the primitive type instead + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/len_zero.fixed b/src/tools/clippy/tests/ui/len_zero.fixed index c16d7a2661615..27319d9c20e8b 100644 --- a/src/tools/clippy/tests/ui/len_zero.fixed +++ b/src/tools/clippy/tests/ui/len_zero.fixed @@ -189,3 +189,40 @@ fn main() { fn test_slice(b: &[u8]) { if !b.is_empty() {} } + +// issue #11992 +fn binop_with_macros() { + macro_rules! len { + ($seq:ident) => { + $seq.len() + }; + } + + macro_rules! compare_to { + ($val:literal) => { + $val + }; + ($val:expr) => {{ $val }}; + } + + macro_rules! zero { + () => { + 0 + }; + } + + let has_is_empty = HasIsEmpty; + // Don't lint, suggesting changes might break macro compatibility. + (len!(has_is_empty) > 0).then(|| println!("This can happen.")); + // Don't lint, suggesting changes might break macro compatibility. + if len!(has_is_empty) == 0 {} + // Don't lint + if has_is_empty.len() == compare_to!(if true { 0 } else { 1 }) {} + // This is fine + if has_is_empty.len() == compare_to!(1) {} + + if has_is_empty.is_empty() {} + if has_is_empty.is_empty() {} + + (!has_is_empty.is_empty()).then(|| println!("This can happen.")); +} diff --git a/src/tools/clippy/tests/ui/len_zero.rs b/src/tools/clippy/tests/ui/len_zero.rs index 5c49a5abf812f..03c05bc6ed7b2 100644 --- a/src/tools/clippy/tests/ui/len_zero.rs +++ b/src/tools/clippy/tests/ui/len_zero.rs @@ -189,3 +189,40 @@ fn main() { fn test_slice(b: &[u8]) { if b.len() != 0 {} } + +// issue #11992 +fn binop_with_macros() { + macro_rules! len { + ($seq:ident) => { + $seq.len() + }; + } + + macro_rules! compare_to { + ($val:literal) => { + $val + }; + ($val:expr) => {{ $val }}; + } + + macro_rules! zero { + () => { + 0 + }; + } + + let has_is_empty = HasIsEmpty; + // Don't lint, suggesting changes might break macro compatibility. + (len!(has_is_empty) > 0).then(|| println!("This can happen.")); + // Don't lint, suggesting changes might break macro compatibility. + if len!(has_is_empty) == 0 {} + // Don't lint + if has_is_empty.len() == compare_to!(if true { 0 } else { 1 }) {} + // This is fine + if has_is_empty.len() == compare_to!(1) {} + + if has_is_empty.len() == compare_to!(0) {} + if has_is_empty.len() == zero!() {} + + (compare_to!(0) < has_is_empty.len()).then(|| println!("This can happen.")); +} diff --git a/src/tools/clippy/tests/ui/len_zero.stderr b/src/tools/clippy/tests/ui/len_zero.stderr index dd07a85d62cac..5c849a2aca646 100644 --- a/src/tools/clippy/tests/ui/len_zero.stderr +++ b/src/tools/clippy/tests/ui/len_zero.stderr @@ -142,5 +142,23 @@ error: length comparison to zero LL | if b.len() != 0 {} | ^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!b.is_empty()` -error: aborting due to 23 previous errors +error: length comparison to zero + --> tests/ui/len_zero.rs:224: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 + | +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 + | +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 diff --git a/src/tools/clippy/tests/ui/let_and_return.fixed b/src/tools/clippy/tests/ui/let_and_return.fixed index b5584fcde8c95..4187019e58944 100644 --- a/src/tools/clippy/tests/ui/let_and_return.fixed +++ b/src/tools/clippy/tests/ui/let_and_return.fixed @@ -203,4 +203,11 @@ fn_in_macro!({ return 1; }); +fn issue9150() -> usize { + let x = 1; + #[cfg(any())] + panic!("can't see me"); + x +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/let_and_return.rs b/src/tools/clippy/tests/ui/let_and_return.rs index f13c7c4e2034b..54444957b7d53 100644 --- a/src/tools/clippy/tests/ui/let_and_return.rs +++ b/src/tools/clippy/tests/ui/let_and_return.rs @@ -203,4 +203,11 @@ fn_in_macro!({ return 1; }); +fn issue9150() -> usize { + let x = 1; + #[cfg(any())] + panic!("can't see me"); + x +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/let_unit.fixed b/src/tools/clippy/tests/ui/let_unit.fixed index 4d41b5e5e5034..20940daffa7a4 100644 --- a/src/tools/clippy/tests/ui/let_unit.fixed +++ b/src/tools/clippy/tests/ui/let_unit.fixed @@ -177,3 +177,21 @@ async fn issue10433() { } pub async fn issue11502(a: ()) {} + +pub fn issue12594() { + fn returns_unit() {} + + fn returns_result(res: T) -> Result { + Ok(res) + } + + fn actual_test() { + // create first a unit value'd value + returns_unit(); + returns_result(()).unwrap(); + returns_result(()).unwrap(); + // make sure we replace only the first variable + let res = 1; + returns_result(res).unwrap(); + } +} diff --git a/src/tools/clippy/tests/ui/let_unit.rs b/src/tools/clippy/tests/ui/let_unit.rs index daa660be25e62..dca66f2e3edb3 100644 --- a/src/tools/clippy/tests/ui/let_unit.rs +++ b/src/tools/clippy/tests/ui/let_unit.rs @@ -177,3 +177,21 @@ async fn issue10433() { } pub async fn issue11502(a: ()) {} + +pub fn issue12594() { + fn returns_unit() {} + + fn returns_result(res: T) -> Result { + Ok(res) + } + + fn actual_test() { + // create first a unit value'd value + let res = returns_unit(); + returns_result(res).unwrap(); + returns_result(res).unwrap(); + // make sure we replace only the first variable + let res = 1; + returns_result(res).unwrap(); + } +} diff --git a/src/tools/clippy/tests/ui/let_unit.stderr b/src/tools/clippy/tests/ui/let_unit.stderr index 0f1f3d7822348..aafb77bcd0d65 100644 --- a/src/tools/clippy/tests/ui/let_unit.stderr +++ b/src/tools/clippy/tests/ui/let_unit.stderr @@ -51,5 +51,24 @@ LL + Some(_) => (), LL + }; | -error: aborting due to 3 previous errors +error: this let-binding has unit value + --> tests/ui/let_unit.rs:190:9 + | +LL | let res = returns_unit(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: omit the `let` binding + | +LL | returns_unit(); + | +help: variable `res` of type `()` can be replaced with explicit `()` + | +LL | returns_result(()).unwrap(); + | ~~ +help: variable `res` of type `()` can be replaced with explicit `()` + | +LL | returns_result(()).unwrap(); + | ~~ + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/manual_assert.edition2018.fixed b/src/tools/clippy/tests/ui/manual_assert.edition2018.fixed index 75beedfa45086..7583578080100 100644 --- a/src/tools/clippy/tests/ui/manual_assert.edition2018.fixed +++ b/src/tools/clippy/tests/ui/manual_assert.edition2018.fixed @@ -66,3 +66,11 @@ fn issue7730(a: u8) { // comment after `panic!` assert!(!(a > 2), "panic with comment"); } + +fn issue12505() { + struct Foo(T); + + impl Foo { + const BAR: () = assert!(!(N == 0), ); + } +} diff --git a/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr b/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr index 57015933d40a9..1eebe1bfe1771 100644 --- a/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr +++ b/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr @@ -82,5 +82,14 @@ help: try instead LL | assert!(!(a > 2), "panic with comment"); | -error: aborting due to 9 previous errors +error: only a `panic!` in `if`-then statement + --> tests/ui/manual_assert.rs:91:25 + | +LL | const BAR: () = if N == 0 { + | _________________________^ +LL | | panic!() +LL | | }; + | |_________^ help: try instead: `assert!(!(N == 0), )` + +error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/manual_assert.edition2021.fixed b/src/tools/clippy/tests/ui/manual_assert.edition2021.fixed index 75beedfa45086..7583578080100 100644 --- a/src/tools/clippy/tests/ui/manual_assert.edition2021.fixed +++ b/src/tools/clippy/tests/ui/manual_assert.edition2021.fixed @@ -66,3 +66,11 @@ fn issue7730(a: u8) { // comment after `panic!` assert!(!(a > 2), "panic with comment"); } + +fn issue12505() { + struct Foo(T); + + impl Foo { + const BAR: () = assert!(!(N == 0), ); + } +} diff --git a/src/tools/clippy/tests/ui/manual_assert.edition2021.stderr b/src/tools/clippy/tests/ui/manual_assert.edition2021.stderr index 57015933d40a9..1eebe1bfe1771 100644 --- a/src/tools/clippy/tests/ui/manual_assert.edition2021.stderr +++ b/src/tools/clippy/tests/ui/manual_assert.edition2021.stderr @@ -82,5 +82,14 @@ help: try instead LL | assert!(!(a > 2), "panic with comment"); | -error: aborting due to 9 previous errors +error: only a `panic!` in `if`-then statement + --> tests/ui/manual_assert.rs:91:25 + | +LL | const BAR: () = if N == 0 { + | _________________________^ +LL | | panic!() +LL | | }; + | |_________^ help: try instead: `assert!(!(N == 0), )` + +error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/manual_assert.rs b/src/tools/clippy/tests/ui/manual_assert.rs index 5979496ca8361..363bafdf05d0b 100644 --- a/src/tools/clippy/tests/ui/manual_assert.rs +++ b/src/tools/clippy/tests/ui/manual_assert.rs @@ -83,3 +83,13 @@ fn issue7730(a: u8) { panic!("panic with comment") // comment after `panic!` } } + +fn issue12505() { + struct Foo(T); + + impl Foo { + const BAR: () = if N == 0 { + panic!() + }; + } +} diff --git a/src/tools/clippy/tests/ui/manual_clamp.fixed b/src/tools/clippy/tests/ui/manual_clamp.fixed index c5355cce8e212..8d57cbbf51bfa 100644 --- a/src/tools/clippy/tests/ui/manual_clamp.fixed +++ b/src/tools/clippy/tests/ui/manual_clamp.fixed @@ -17,48 +17,171 @@ const CONST_F64_MIN: f64 = 4.0; fn main() { let (input, min, max) = (0, -2, 3); - // Lint - let x0 = input.clamp(min, max); + // Min and max are not const, so this shouldn't trigger the lint. + let x0 = if max < input { + max + } else if min > input { + min + } else { + input + }; + + let x1 = if input > max { + max + } else if input < min { + min + } else { + input + }; + + let x2 = if input < min { + min + } else if input > max { + max + } else { + input + }; - let x1 = input.clamp(min, max); + let x3 = if min > input { + min + } else if max < input { + max + } else { + input + }; - let x2 = input.clamp(min, max); + let x4 = input.max(min).min(max); - let x3 = input.clamp(min, max); + let x5 = input.min(max).max(min); + + let x6 = match input { + x if x > max => max, + x if x < min => min, + x => x, + }; - let x4 = input.clamp(min, max); + let x7 = match input { + x if x < min => min, + x if x > max => max, + x => x, + }; + + let x8 = match input { + x if max < x => max, + x if min > x => min, + x => x, + }; + + let mut x9 = input; + if x9 < min { + x9 = min; + } + if x9 > max { + x9 = max; + } + + let x10 = match input { + x if min > x => min, + x if max < x => max, + x => x, + }; + + let mut x11 = input; + let _ = 1; + if x11 > max { + x11 = max; + } + if x11 < min { + x11 = min; + } + + let mut x12 = input; + if min > x12 { + x12 = min; + } + if max < x12 { + x12 = max; + } + + let mut x13 = input; + if max < x13 { + x13 = max; + } + if min > x13 { + x13 = min; + } + + { + let (input, min, max) = (0.0f64, -2.0, 3.0); + let x14 = if input > max { + max + } else if input < min { + min + } else { + input + }; + } + let mut x15 = input; + if x15 < min { + x15 = min; + } else if x15 > max { + x15 = max; + } + + // It's important this be the last set of statements + let mut x16 = input; + if max < x16 { + x16 = max; + } + if min > x16 { + x16 = min; + } +} + +fn const_main() { + let input = 0; + // Min and max are const, so this should trigger the lint. + let x0 = input.clamp(CONST_MIN, CONST_MAX); + + let x1 = input.clamp(CONST_MIN, CONST_MAX); + + let x2 = input.clamp(CONST_MIN, CONST_MAX); + + let x3 = input.clamp(CONST_MIN, CONST_MAX); + + let x4 = input.clamp(CONST_MIN, CONST_MAX); //~^ ERROR: clamp-like pattern without using clamp function //~| NOTE: clamp will panic if max < min - let x5 = input.clamp(min, max); + let x5 = input.clamp(CONST_MIN, CONST_MAX); //~^ ERROR: clamp-like pattern without using clamp function //~| NOTE: clamp will panic if max < min - let x6 = input.clamp(min, max); + let x6 = input.clamp(CONST_MIN, CONST_MAX); - let x7 = input.clamp(min, max); + let x7 = input.clamp(CONST_MIN, CONST_MAX); - let x8 = input.clamp(min, max); + let x8 = input.clamp(CONST_MIN, CONST_MAX); let mut x9 = input; - x9 = x9.clamp(min, max); + x9 = x9.clamp(CONST_MIN, CONST_MAX); - let x10 = input.clamp(min, max); + let x10 = input.clamp(CONST_MIN, CONST_MAX); let mut x11 = input; let _ = 1; - x11 = x11.clamp(min, max); + x11 = x11.clamp(CONST_MIN, CONST_MAX); let mut x12 = input; - x12 = x12.clamp(min, max); + x12 = x12.clamp(CONST_MIN, CONST_MAX); let mut x13 = input; - x13 = x13.clamp(min, max); + x13 = x13.clamp(CONST_MIN, CONST_MAX); let x14 = input.clamp(CONST_MIN, CONST_MAX); { - let (input, min, max) = (0.0f64, -2.0, 3.0); - let x15 = input.clamp(min, max); + let input = 0.0f64; + let x15 = input.clamp(CONST_F64_MIN, CONST_F64_MAX); } { let input: i32 = cmp_min_max(1); @@ -114,108 +237,128 @@ fn main() { //~| NOTE: clamp will panic if max < min, min.is_nan(), or max.is_nan() } let mut x32 = input; - x32 = x32.clamp(min, max); + x32 = x32.clamp(CONST_MIN, CONST_MAX); - // It's important this be the last set of statements + // Flip the script, swap the places of min and max. Make sure this doesn't + // trigger when clamp would be guaranteed to panic. let mut x33 = input; - x33 = x33.clamp(min, max); + if x33 < CONST_MAX { + x33 = CONST_MAX; + } else if x33 > CONST_MIN { + x33 = CONST_MIN; + } + + // Do it again for NaN + #[allow(invalid_nan_comparisons)] + { + let mut x34 = input as f64; + if x34 < f64::NAN { + x34 = f64::NAN; + } else if x34 > CONST_F64_MAX { + x34 = CONST_F64_MAX; + } + } + + // It's important this be the last set of statements + let mut x35 = input; + x35 = x35.clamp(CONST_MIN, CONST_MAX); } // This code intentionally nonsense. fn no_lint() { - let (input, min, max) = (0, -2, 3); - let x0 = if max < input { - max - } else if min > input { - max + let input = 0; + let x0 = if CONST_MAX < input { + CONST_MAX + } else if CONST_MIN > input { + CONST_MAX } else { - min + CONST_MIN }; - let x1 = if input > max { - max - } else if input > min { - min + let x1 = if input > CONST_MAX { + CONST_MAX + } else if input > CONST_MIN { + CONST_MIN } else { - max + CONST_MAX }; - let x2 = if max < min { - min - } else if input > max { + let x2 = if CONST_MAX < CONST_MIN { + CONST_MIN + } else if input > CONST_MAX { input } else { input }; - let x3 = if min > input { + let x3 = if CONST_MIN > input { input - } else if max < input { - max + } else if CONST_MAX < input { + CONST_MAX } else { - max + CONST_MAX }; let x6 = match input { - x if x < max => x, - x if x < min => x, + x if x < CONST_MAX => x, + x if x < CONST_MIN => x, x => x, }; let x7 = match input { - x if x < min => max, - x if x > max => min, + x if x < CONST_MIN => CONST_MAX, + x if x > CONST_MAX => CONST_MIN, x => x, }; let x8 = match input { - x if max > x => max, - x if min > x => min, + x if CONST_MAX > x => CONST_MAX, + x if CONST_MIN > x => CONST_MIN, x => x, }; let mut x9 = input; - if x9 > min { - x9 = min; + if x9 > CONST_MIN { + x9 = CONST_MIN; } - if x9 > max { - x9 = max; + if x9 > CONST_MAX { + x9 = CONST_MAX; } let x10 = match input { - x if min > x => min, - x if max < x => max, - x => min, + x if CONST_MIN > x => CONST_MIN, + x if CONST_MAX < x => CONST_MAX, + x => CONST_MIN, }; let mut x11 = input; - if x11 > max { - x11 = min; + if x11 > CONST_MAX { + x11 = CONST_MIN; } - if x11 < min { - x11 = max; + if x11 < CONST_MIN { + x11 = CONST_MAX; } let mut x12 = input; - if min > x12 { - x12 = max * 3; + if CONST_MIN > x12 { + x12 = CONST_MAX * 3; } - if max < x12 { - x12 = min; + if CONST_MAX < x12 { + x12 = CONST_MIN; } let mut x13 = input; - if max < x13 { - let x13 = max; + if CONST_MAX < x13 { + let x13 = CONST_MAX; } - if min > x13 { - x13 = min; + if CONST_MIN > x13 { + x13 = CONST_MIN; } let mut x14 = input; - if x14 < min { + if x14 < CONST_MIN { x14 = 3; - } else if x14 > max { - x14 = max; + } else if x14 > CONST_MAX { + x14 = CONST_MAX; } { let input: i32 = cmp_min_max(1); @@ -272,8 +415,8 @@ fn msrv_1_49() { #[clippy::msrv = "1.50"] fn msrv_1_50() { - let (input, min, max) = (0, -1, 2); - let _ = input.clamp(min, max); + let input = 0; + let _ = input.clamp(CONST_MIN, CONST_MAX); } const fn _const() { diff --git a/src/tools/clippy/tests/ui/manual_clamp.rs b/src/tools/clippy/tests/ui/manual_clamp.rs index cacb40ae027f3..4d2a0c47bbd7c 100644 --- a/src/tools/clippy/tests/ui/manual_clamp.rs +++ b/src/tools/clippy/tests/ui/manual_clamp.rs @@ -17,10 +17,8 @@ const CONST_F64_MIN: f64 = 4.0; fn main() { let (input, min, max) = (0, -2, 3); - // Lint + // Min and max are not const, so this shouldn't trigger the lint. let x0 = if max < input { - //~^ ERROR: clamp-like pattern without using clamp function - //~| NOTE: clamp will panic if max < min max } else if min > input { min @@ -29,8 +27,6 @@ fn main() { }; let x1 = if input > max { - //~^ ERROR: clamp-like pattern without using clamp function - //~| NOTE: clamp will panic if max < min max } else if input < min { min @@ -39,8 +35,6 @@ fn main() { }; let x2 = if input < min { - //~^ ERROR: clamp-like pattern without using clamp function - //~| NOTE: clamp will panic if max < min min } else if input > max { max @@ -49,8 +43,6 @@ fn main() { }; let x3 = if min > input { - //~^ ERROR: clamp-like pattern without using clamp function - //~| NOTE: clamp will panic if max < min min } else if max < input { max @@ -59,32 +51,22 @@ fn main() { }; let x4 = input.max(min).min(max); - //~^ ERROR: clamp-like pattern without using clamp function - //~| NOTE: clamp will panic if max < min let x5 = input.min(max).max(min); - //~^ ERROR: clamp-like pattern without using clamp function - //~| NOTE: clamp will panic if max < min let x6 = match input { - //~^ ERROR: clamp-like pattern without using clamp function - //~| NOTE: clamp will panic if max < min x if x > max => max, x if x < min => min, x => x, }; let x7 = match input { - //~^ ERROR: clamp-like pattern without using clamp function - //~| NOTE: clamp will panic if max < min x if x < min => min, x if x > max => max, x => x, }; let x8 = match input { - //~^ ERROR: clamp-like pattern without using clamp function - //~| NOTE: clamp will panic if max < min x if max < x => max, x if min > x => min, x => x, @@ -92,8 +74,6 @@ fn main() { let mut x9 = input; if x9 < min { - //~^ ERROR: clamp-like pattern without using clamp function - //~| NOTE: clamp will panic if max < min x9 = min; } if x9 > max { @@ -101,8 +81,6 @@ fn main() { } let x10 = match input { - //~^ ERROR: clamp-like pattern without using clamp function - //~| NOTE: clamp will panic if max < min x if min > x => min, x if max < x => max, x => x, @@ -111,8 +89,6 @@ fn main() { let mut x11 = input; let _ = 1; if x11 > max { - //~^ ERROR: clamp-like pattern without using clamp function - //~| NOTE: clamp will panic if max < min x11 = max; } if x11 < min { @@ -121,8 +97,6 @@ fn main() { let mut x12 = input; if min > x12 { - //~^ ERROR: clamp-like pattern without using clamp function - //~| NOTE: clamp will panic if max < min x12 = min; } if max < x12 { @@ -131,14 +105,163 @@ fn main() { let mut x13 = input; if max < x13 { - //~^ ERROR: clamp-like pattern without using clamp function - //~| NOTE: clamp will panic if max < min x13 = max; } if min > x13 { x13 = min; } + { + let (input, min, max) = (0.0f64, -2.0, 3.0); + let x14 = if input > max { + max + } else if input < min { + min + } else { + input + }; + } + let mut x15 = input; + if x15 < min { + x15 = min; + } else if x15 > max { + x15 = max; + } + + // It's important this be the last set of statements + let mut x16 = input; + if max < x16 { + x16 = max; + } + if min > x16 { + x16 = min; + } +} + +fn const_main() { + let input = 0; + // Min and max are const, so this should trigger the lint. + let x0 = if CONST_MAX < input { + //~^ ERROR: clamp-like pattern without using clamp function + //~| NOTE: clamp will panic if max < min + CONST_MAX + } else if CONST_MIN > input { + CONST_MIN + } else { + input + }; + + let x1 = if input > CONST_MAX { + //~^ ERROR: clamp-like pattern without using clamp function + //~| NOTE: clamp will panic if max < min + CONST_MAX + } else if input < CONST_MIN { + CONST_MIN + } else { + input + }; + + let x2 = if input < CONST_MIN { + //~^ ERROR: clamp-like pattern without using clamp function + //~| NOTE: clamp will panic if max < min + CONST_MIN + } else if input > CONST_MAX { + CONST_MAX + } else { + input + }; + + let x3 = if CONST_MIN > input { + //~^ ERROR: clamp-like pattern without using clamp function + //~| NOTE: clamp will panic if max < min + CONST_MIN + } else if CONST_MAX < input { + CONST_MAX + } else { + input + }; + + let x4 = input.max(CONST_MIN).min(CONST_MAX); + //~^ ERROR: clamp-like pattern without using clamp function + //~| NOTE: clamp will panic if max < min + + let x5 = input.min(CONST_MAX).max(CONST_MIN); + //~^ ERROR: clamp-like pattern without using clamp function + //~| NOTE: clamp will panic if max < min + + let x6 = match input { + //~^ ERROR: clamp-like pattern without using clamp function + //~| NOTE: clamp will panic if max < min + x if x > CONST_MAX => CONST_MAX, + x if x < CONST_MIN => CONST_MIN, + x => x, + }; + + let x7 = match input { + //~^ ERROR: clamp-like pattern without using clamp function + //~| NOTE: clamp will panic if max < min + x if x < CONST_MIN => CONST_MIN, + x if x > CONST_MAX => CONST_MAX, + x => x, + }; + + let x8 = match input { + //~^ ERROR: clamp-like pattern without using clamp function + //~| NOTE: clamp will panic if max < min + x if CONST_MAX < x => CONST_MAX, + x if CONST_MIN > x => CONST_MIN, + x => x, + }; + + let mut x9 = input; + if x9 < CONST_MIN { + //~^ ERROR: clamp-like pattern without using clamp function + //~| NOTE: clamp will panic if max < min + x9 = CONST_MIN; + } + if x9 > CONST_MAX { + x9 = CONST_MAX; + } + + let x10 = match input { + //~^ ERROR: clamp-like pattern without using clamp function + //~| NOTE: clamp will panic if max < min + x if CONST_MIN > x => CONST_MIN, + x if CONST_MAX < x => CONST_MAX, + x => x, + }; + + let mut x11 = input; + let _ = 1; + if x11 > CONST_MAX { + //~^ ERROR: clamp-like pattern without using clamp function + //~| NOTE: clamp will panic if max < min + x11 = CONST_MAX; + } + if x11 < CONST_MIN { + x11 = CONST_MIN; + } + + let mut x12 = input; + if CONST_MIN > x12 { + //~^ ERROR: clamp-like pattern without using clamp function + //~| NOTE: clamp will panic if max < min + x12 = CONST_MIN; + } + if CONST_MAX < x12 { + x12 = CONST_MAX; + } + + let mut x13 = input; + if CONST_MAX < x13 { + //~^ ERROR: clamp-like pattern without using clamp function + //~| NOTE: clamp will panic if max < min + x13 = CONST_MAX; + } + if CONST_MIN > x13 { + x13 = CONST_MIN; + } + let x14 = if input > CONST_MAX { //~^ ERROR: clamp-like pattern without using clamp function //~| NOTE: clamp will panic if max < min @@ -149,13 +272,13 @@ fn main() { input }; { - let (input, min, max) = (0.0f64, -2.0, 3.0); - let x15 = if input > max { + let input = 0.0f64; + let x15 = if input > CONST_F64_MAX { //~^ ERROR: clamp-like pattern without using clamp function - //~| NOTE: clamp will panic if max < min, min.is_nan(), or max.is_nan() - max - } else if input < min { - min + //~| NOTE: clamp will panic if max < min + CONST_F64_MAX + } else if input < CONST_F64_MIN { + CONST_F64_MIN } else { input }; @@ -214,121 +337,141 @@ fn main() { //~| NOTE: clamp will panic if max < min, min.is_nan(), or max.is_nan() } let mut x32 = input; - if x32 < min { + if x32 < CONST_MIN { //~^ ERROR: clamp-like pattern without using clamp function //~| NOTE: clamp will panic if max < min - x32 = min; - } else if x32 > max { - x32 = max; + x32 = CONST_MIN; + } else if x32 > CONST_MAX { + x32 = CONST_MAX; } - // It's important this be the last set of statements + // Flip the script, swap the places of min and max. Make sure this doesn't + // trigger when clamp would be guaranteed to panic. let mut x33 = input; - if max < x33 { + if x33 < CONST_MAX { + x33 = CONST_MAX; + } else if x33 > CONST_MIN { + x33 = CONST_MIN; + } + + // Do it again for NaN + #[allow(invalid_nan_comparisons)] + { + let mut x34 = input as f64; + if x34 < f64::NAN { + x34 = f64::NAN; + } else if x34 > CONST_F64_MAX { + x34 = CONST_F64_MAX; + } + } + + // It's important this be the last set of statements + let mut x35 = input; + if CONST_MAX < x35 { //~^ ERROR: clamp-like pattern without using clamp function //~| NOTE: clamp will panic if max < min - x33 = max; + x35 = CONST_MAX; } - if min > x33 { - x33 = min; + if CONST_MIN > x35 { + x35 = CONST_MIN; } } // This code intentionally nonsense. fn no_lint() { - let (input, min, max) = (0, -2, 3); - let x0 = if max < input { - max - } else if min > input { - max + let input = 0; + let x0 = if CONST_MAX < input { + CONST_MAX + } else if CONST_MIN > input { + CONST_MAX } else { - min + CONST_MIN }; - let x1 = if input > max { - max - } else if input > min { - min + let x1 = if input > CONST_MAX { + CONST_MAX + } else if input > CONST_MIN { + CONST_MIN } else { - max + CONST_MAX }; - let x2 = if max < min { - min - } else if input > max { + let x2 = if CONST_MAX < CONST_MIN { + CONST_MIN + } else if input > CONST_MAX { input } else { input }; - let x3 = if min > input { + let x3 = if CONST_MIN > input { input - } else if max < input { - max + } else if CONST_MAX < input { + CONST_MAX } else { - max + CONST_MAX }; let x6 = match input { - x if x < max => x, - x if x < min => x, + x if x < CONST_MAX => x, + x if x < CONST_MIN => x, x => x, }; let x7 = match input { - x if x < min => max, - x if x > max => min, + x if x < CONST_MIN => CONST_MAX, + x if x > CONST_MAX => CONST_MIN, x => x, }; let x8 = match input { - x if max > x => max, - x if min > x => min, + x if CONST_MAX > x => CONST_MAX, + x if CONST_MIN > x => CONST_MIN, x => x, }; let mut x9 = input; - if x9 > min { - x9 = min; + if x9 > CONST_MIN { + x9 = CONST_MIN; } - if x9 > max { - x9 = max; + if x9 > CONST_MAX { + x9 = CONST_MAX; } let x10 = match input { - x if min > x => min, - x if max < x => max, - x => min, + x if CONST_MIN > x => CONST_MIN, + x if CONST_MAX < x => CONST_MAX, + x => CONST_MIN, }; let mut x11 = input; - if x11 > max { - x11 = min; + if x11 > CONST_MAX { + x11 = CONST_MIN; } - if x11 < min { - x11 = max; + if x11 < CONST_MIN { + x11 = CONST_MAX; } let mut x12 = input; - if min > x12 { - x12 = max * 3; + if CONST_MIN > x12 { + x12 = CONST_MAX * 3; } - if max < x12 { - x12 = min; + if CONST_MAX < x12 { + x12 = CONST_MIN; } let mut x13 = input; - if max < x13 { - let x13 = max; + if CONST_MAX < x13 { + let x13 = CONST_MAX; } - if min > x13 { - x13 = min; + if CONST_MIN > x13 { + x13 = CONST_MIN; } let mut x14 = input; - if x14 < min { + if x14 < CONST_MIN { x14 = 3; - } else if x14 > max { - x14 = max; + } else if x14 > CONST_MAX { + x14 = CONST_MAX; } { let input: i32 = cmp_min_max(1); @@ -385,13 +528,13 @@ fn msrv_1_49() { #[clippy::msrv = "1.50"] fn msrv_1_50() { - let (input, min, max) = (0, -1, 2); - let _ = if input < min { + let input = 0; + let _ = if input > CONST_MAX { //~^ ERROR: clamp-like pattern without using clamp function //~| NOTE: clamp will panic if max < min - min - } else if input > max { - max + CONST_MAX + } else if input < CONST_MIN { + CONST_MIN } else { input }; diff --git a/src/tools/clippy/tests/ui/manual_clamp.stderr b/src/tools/clippy/tests/ui/manual_clamp.stderr index 52c816f2b347b..459d46796d875 100644 --- a/src/tools/clippy/tests/ui/manual_clamp.stderr +++ b/src/tools/clippy/tests/ui/manual_clamp.stderr @@ -1,213 +1,213 @@ error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:94:5 + --> tests/ui/manual_clamp.rs:217:5 | -LL | / if x9 < min { +LL | / if x9 < CONST_MIN { LL | | LL | | -LL | | x9 = min; +LL | | x9 = CONST_MIN; ... | -LL | | x9 = max; +LL | | x9 = CONST_MAX; LL | | } - | |_____^ help: replace with clamp: `x9 = x9.clamp(min, max);` + | |_____^ help: replace with clamp: `x9 = x9.clamp(CONST_MIN, CONST_MAX);` | = note: clamp will panic if max < min = note: `-D clippy::manual-clamp` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::manual_clamp)]` error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:113:5 + --> tests/ui/manual_clamp.rs:236:5 | -LL | / if x11 > max { +LL | / if x11 > CONST_MAX { LL | | LL | | -LL | | x11 = max; +LL | | x11 = CONST_MAX; ... | -LL | | x11 = min; +LL | | x11 = CONST_MIN; LL | | } - | |_____^ help: replace with clamp: `x11 = x11.clamp(min, max);` + | |_____^ help: replace with clamp: `x11 = x11.clamp(CONST_MIN, CONST_MAX);` | = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:123:5 + --> tests/ui/manual_clamp.rs:246:5 | -LL | / if min > x12 { +LL | / if CONST_MIN > x12 { LL | | LL | | -LL | | x12 = min; +LL | | x12 = CONST_MIN; ... | -LL | | x12 = max; +LL | | x12 = CONST_MAX; LL | | } - | |_____^ help: replace with clamp: `x12 = x12.clamp(min, max);` + | |_____^ help: replace with clamp: `x12 = x12.clamp(CONST_MIN, CONST_MAX);` | = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:133:5 + --> tests/ui/manual_clamp.rs:256:5 | -LL | / if max < x13 { +LL | / if CONST_MAX < x13 { LL | | LL | | -LL | | x13 = max; +LL | | x13 = CONST_MAX; ... | -LL | | x13 = min; +LL | | x13 = CONST_MIN; LL | | } - | |_____^ help: replace with clamp: `x13 = x13.clamp(min, max);` + | |_____^ help: replace with clamp: `x13 = x13.clamp(CONST_MIN, CONST_MAX);` | = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:227:5 + --> tests/ui/manual_clamp.rs:370:5 | -LL | / if max < x33 { +LL | / if CONST_MAX < x35 { LL | | LL | | -LL | | x33 = max; +LL | | x35 = CONST_MAX; ... | -LL | | x33 = min; +LL | | x35 = CONST_MIN; LL | | } - | |_____^ help: replace with clamp: `x33 = x33.clamp(min, max);` + | |_____^ help: replace with clamp: `x35 = x35.clamp(CONST_MIN, CONST_MAX);` | = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:21:14 + --> tests/ui/manual_clamp.rs:144:14 | -LL | let x0 = if max < input { +LL | let x0 = if CONST_MAX < input { | ______________^ LL | | LL | | -LL | | max +LL | | CONST_MAX ... | LL | | input LL | | }; - | |_____^ help: replace with clamp: `input.clamp(min, max)` + | |_____^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` | = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:31:14 + --> tests/ui/manual_clamp.rs:154:14 | -LL | let x1 = if input > max { +LL | let x1 = if input > CONST_MAX { | ______________^ LL | | LL | | -LL | | max +LL | | CONST_MAX ... | LL | | input LL | | }; - | |_____^ help: replace with clamp: `input.clamp(min, max)` + | |_____^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` | = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:41:14 + --> tests/ui/manual_clamp.rs:164:14 | -LL | let x2 = if input < min { +LL | let x2 = if input < CONST_MIN { | ______________^ LL | | LL | | -LL | | min +LL | | CONST_MIN ... | LL | | input LL | | }; - | |_____^ help: replace with clamp: `input.clamp(min, max)` + | |_____^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` | = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:51:14 + --> tests/ui/manual_clamp.rs:174:14 | -LL | let x3 = if min > input { +LL | let x3 = if CONST_MIN > input { | ______________^ LL | | LL | | -LL | | min +LL | | CONST_MIN ... | LL | | input LL | | }; - | |_____^ help: replace with clamp: `input.clamp(min, max)` + | |_____^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` | = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:61:14 + --> tests/ui/manual_clamp.rs:184:14 | -LL | let x4 = input.max(min).min(max); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)` +LL | let x4 = input.max(CONST_MIN).min(CONST_MAX); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` | = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:65:14 + --> tests/ui/manual_clamp.rs:188:14 | -LL | let x5 = input.min(max).max(min); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)` +LL | let x5 = input.min(CONST_MAX).max(CONST_MIN); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` | = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:69:14 + --> tests/ui/manual_clamp.rs:192:14 | LL | let x6 = match input { | ______________^ LL | | LL | | -LL | | x if x > max => max, -LL | | x if x < min => min, +LL | | x if x > CONST_MAX => CONST_MAX, +LL | | x if x < CONST_MIN => CONST_MIN, LL | | x => x, LL | | }; - | |_____^ help: replace with clamp: `input.clamp(min, max)` + | |_____^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` | = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:77:14 + --> tests/ui/manual_clamp.rs:200:14 | LL | let x7 = match input { | ______________^ LL | | LL | | -LL | | x if x < min => min, -LL | | x if x > max => max, +LL | | x if x < CONST_MIN => CONST_MIN, +LL | | x if x > CONST_MAX => CONST_MAX, LL | | x => x, LL | | }; - | |_____^ help: replace with clamp: `input.clamp(min, max)` + | |_____^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` | = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:85:14 + --> tests/ui/manual_clamp.rs:208:14 | LL | let x8 = match input { | ______________^ LL | | LL | | -LL | | x if max < x => max, -LL | | x if min > x => min, +LL | | x if CONST_MAX < x => CONST_MAX, +LL | | x if CONST_MIN > x => CONST_MIN, LL | | x => x, LL | | }; - | |_____^ help: replace with clamp: `input.clamp(min, max)` + | |_____^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` | = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:103:15 + --> tests/ui/manual_clamp.rs:226:15 | LL | let x10 = match input { | _______________^ LL | | LL | | -LL | | x if min > x => min, -LL | | x if max < x => max, +LL | | x if CONST_MIN > x => CONST_MIN, +LL | | x if CONST_MAX < x => CONST_MAX, LL | | x => x, LL | | }; - | |_____^ help: replace with clamp: `input.clamp(min, max)` + | |_____^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` | = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:142:15 + --> tests/ui/manual_clamp.rs:265:15 | LL | let x14 = if input > CONST_MAX { | _______________^ @@ -222,23 +222,23 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:153:19 + --> tests/ui/manual_clamp.rs:276:19 | -LL | let x15 = if input > max { +LL | let x15 = if input > CONST_F64_MAX { | ___________________^ LL | | LL | | -LL | | max +LL | | CONST_F64_MAX ... | LL | | input LL | | }; - | |_________^ help: replace with clamp: `input.clamp(min, max)` + | |_________^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` | = note: clamp will panic if max < min, min.is_nan(), or max.is_nan() = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:166:19 + --> tests/ui/manual_clamp.rs:289:19 | LL | let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -246,7 +246,7 @@ LL | let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:169:19 + --> tests/ui/manual_clamp.rs:292:19 | LL | let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -254,7 +254,7 @@ LL | let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:172:19 + --> tests/ui/manual_clamp.rs:295:19 | LL | let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -262,7 +262,7 @@ LL | let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:175:19 + --> tests/ui/manual_clamp.rs:298:19 | LL | let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -270,7 +270,7 @@ LL | let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:178:19 + --> tests/ui/manual_clamp.rs:301:19 | LL | let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -278,7 +278,7 @@ LL | let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:181:19 + --> tests/ui/manual_clamp.rs:304:19 | LL | let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -286,7 +286,7 @@ LL | let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:184:19 + --> tests/ui/manual_clamp.rs:307:19 | LL | let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -294,7 +294,7 @@ LL | let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:187:19 + --> tests/ui/manual_clamp.rs:310:19 | LL | let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -302,7 +302,7 @@ LL | let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:191:19 + --> tests/ui/manual_clamp.rs:314:19 | LL | let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -311,7 +311,7 @@ LL | let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:194:19 + --> tests/ui/manual_clamp.rs:317:19 | LL | let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -320,7 +320,7 @@ LL | let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:197:19 + --> tests/ui/manual_clamp.rs:320:19 | LL | let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -329,7 +329,7 @@ LL | let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:200:19 + --> tests/ui/manual_clamp.rs:323:19 | LL | let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -338,7 +338,7 @@ LL | let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:203:19 + --> tests/ui/manual_clamp.rs:326:19 | LL | let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -347,7 +347,7 @@ LL | let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:206:19 + --> tests/ui/manual_clamp.rs:329:19 | LL | let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -356,7 +356,7 @@ LL | let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:209:19 + --> tests/ui/manual_clamp.rs:332:19 | LL | let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -365,7 +365,7 @@ LL | let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:212:19 + --> tests/ui/manual_clamp.rs:335:19 | LL | let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -374,31 +374,31 @@ LL | let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:217:5 + --> tests/ui/manual_clamp.rs:340:5 | -LL | / if x32 < min { +LL | / if x32 < CONST_MIN { LL | | LL | | -LL | | x32 = min; -LL | | } else if x32 > max { -LL | | x32 = max; +LL | | x32 = CONST_MIN; +LL | | } else if x32 > CONST_MAX { +LL | | x32 = CONST_MAX; LL | | } - | |_____^ help: replace with clamp: `x32 = x32.clamp(min, max);` + | |_____^ help: replace with clamp: `x32 = x32.clamp(CONST_MIN, CONST_MAX);` | = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:389:13 + --> tests/ui/manual_clamp.rs:532:13 | -LL | let _ = if input < min { +LL | let _ = if input > CONST_MAX { | _____________^ LL | | LL | | -LL | | min +LL | | CONST_MAX ... | LL | | input LL | | }; - | |_____^ help: replace with clamp: `input.clamp(min, max)` + | |_____^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` | = note: clamp will panic if max < min diff --git a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed index 8218f10881a75..41a32df32ee40 100644 --- a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed +++ b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed @@ -1,4 +1,4 @@ -#![allow(unused_imports)] +#![allow(clippy::legacy_numeric_constants, unused_imports)] use std::{i128, i32, u128, u32}; diff --git a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs index 60022b54b02d5..3a6b32d806904 100644 --- a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs +++ b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs @@ -1,4 +1,4 @@ -#![allow(unused_imports)] +#![allow(clippy::legacy_numeric_constants, unused_imports)] use std::{i128, i32, u128, u32}; diff --git a/src/tools/clippy/tests/ui/manual_swap_auto_fix.fixed b/src/tools/clippy/tests/ui/manual_swap_auto_fix.fixed new file mode 100644 index 0000000000000..28466ff3f9b4d --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_swap_auto_fix.fixed @@ -0,0 +1,57 @@ +#![warn(clippy::manual_swap)] +#![no_main] + +fn swap1() { + let mut v = [3, 2, 1, 0]; + let index = v[0]; + v.swap(0, index); +} + +fn swap2() { + let mut v = [3, 2, 1, 0]; + let tmp = v[0]; + v.swap(0, 1); + // check not found in this scope. + let _ = tmp; +} + +fn swap3() { + let mut v = [3, 2]; + let i1 = 0; + let i2 = 1; + v.swap(i1, i2); +} + +fn swap4() { + let mut v = [3, 2, 1]; + let i1 = 0; + let i2 = 1; + v.swap(i1, i2 + 1); +} + +fn swap5() { + let mut v = [0, 1, 2, 3]; + let i1 = 0; + let i2 = 1; + v.swap(i1, i2 + 1); +} + +fn swap6() { + let mut v = [0, 1, 2, 3]; + let index = v[0]; + v.swap(0, index + 1); +} + +fn swap7() { + let mut v = [0, 1, 2, 3]; + let i1 = 0; + let i2 = 6; + v.swap(i1 * 3, i2 / 2); +} + +fn swap8() { + let mut v = [1, 2, 3, 4]; + let i1 = 1; + let i2 = 1; + v.swap(i1 + i2, i2); +} diff --git a/src/tools/clippy/tests/ui/manual_swap_auto_fix.rs b/src/tools/clippy/tests/ui/manual_swap_auto_fix.rs new file mode 100644 index 0000000000000..702a9e67d3d59 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_swap_auto_fix.rs @@ -0,0 +1,72 @@ +#![warn(clippy::manual_swap)] +#![no_main] + +fn swap1() { + let mut v = [3, 2, 1, 0]; + let index = v[0]; + //~^ ERROR: this looks like you are swapping elements of `v` manually + v[0] = v[index]; + v[index] = index; +} + +fn swap2() { + let mut v = [3, 2, 1, 0]; + let tmp = v[0]; + v[0] = v[1]; + v[1] = tmp; + // check not found in this scope. + let _ = tmp; +} + +fn swap3() { + let mut v = [3, 2]; + let i1 = 0; + let i2 = 1; + let temp = v[i1]; + v[i1] = v[i2]; + v[i2] = temp; +} + +fn swap4() { + let mut v = [3, 2, 1]; + let i1 = 0; + let i2 = 1; + let temp = v[i1]; + v[i1] = v[i2 + 1]; + v[i2 + 1] = temp; +} + +fn swap5() { + let mut v = [0, 1, 2, 3]; + let i1 = 0; + let i2 = 1; + let temp = v[i1]; + v[i1] = v[i2 + 1]; + v[i2 + 1] = temp; +} + +fn swap6() { + let mut v = [0, 1, 2, 3]; + let index = v[0]; + //~^ ERROR: this looks like you are swapping elements of `v` manually + v[0] = v[index + 1]; + v[index + 1] = index; +} + +fn swap7() { + let mut v = [0, 1, 2, 3]; + let i1 = 0; + let i2 = 6; + let tmp = v[i1 * 3]; + v[i1 * 3] = v[i2 / 2]; + v[i2 / 2] = tmp; +} + +fn swap8() { + let mut v = [1, 2, 3, 4]; + let i1 = 1; + let i2 = 1; + let tmp = v[i1 + i2]; + v[i1 + i2] = v[i2]; + v[i2] = tmp; +} diff --git a/src/tools/clippy/tests/ui/manual_swap_auto_fix.stderr b/src/tools/clippy/tests/ui/manual_swap_auto_fix.stderr new file mode 100644 index 0000000000000..eecfcd3977beb --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_swap_auto_fix.stderr @@ -0,0 +1,88 @@ +error: this looks like you are swapping elements of `v` manually + --> tests/ui/manual_swap_auto_fix.rs:6:5 + | +LL | / let index = v[0]; +LL | | +LL | | v[0] = v[index]; +LL | | v[index] = index; + | |_____________________^ + | + = note: `-D clippy::manual-swap` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_swap)]` +help: try + | +LL ~ let index = v[0]; +LL + v.swap(0, index); + | + +error: this looks like you are swapping elements of `v` manually + --> tests/ui/manual_swap_auto_fix.rs:14:5 + | +LL | / let tmp = v[0]; +LL | | v[0] = v[1]; +LL | | v[1] = tmp; + | |_______________^ + | +help: try + | +LL ~ let tmp = v[0]; +LL + v.swap(0, 1); + | + +error: this looks like you are swapping elements of `v` manually + --> tests/ui/manual_swap_auto_fix.rs:25:5 + | +LL | / let temp = v[i1]; +LL | | v[i1] = v[i2]; +LL | | v[i2] = temp; + | |_________________^ help: try: `v.swap(i1, i2);` + +error: this looks like you are swapping elements of `v` manually + --> tests/ui/manual_swap_auto_fix.rs:34:5 + | +LL | / let temp = v[i1]; +LL | | v[i1] = v[i2 + 1]; +LL | | v[i2 + 1] = temp; + | |_____________________^ help: try: `v.swap(i1, i2 + 1);` + +error: this looks like you are swapping elements of `v` manually + --> tests/ui/manual_swap_auto_fix.rs:43:5 + | +LL | / let temp = v[i1]; +LL | | v[i1] = v[i2 + 1]; +LL | | v[i2 + 1] = temp; + | |_____________________^ help: try: `v.swap(i1, i2 + 1);` + +error: this looks like you are swapping elements of `v` manually + --> tests/ui/manual_swap_auto_fix.rs:50:5 + | +LL | / let index = v[0]; +LL | | +LL | | v[0] = v[index + 1]; +LL | | v[index + 1] = index; + | |_________________________^ + | +help: try + | +LL ~ let index = v[0]; +LL + v.swap(0, index + 1); + | + +error: this looks like you are swapping elements of `v` manually + --> tests/ui/manual_swap_auto_fix.rs:60:5 + | +LL | / let tmp = v[i1 * 3]; +LL | | v[i1 * 3] = v[i2 / 2]; +LL | | v[i2 / 2] = tmp; + | |____________________^ help: try: `v.swap(i1 * 3, i2 / 2);` + +error: this looks like you are swapping elements of `v` manually + --> tests/ui/manual_swap_auto_fix.rs:69:5 + | +LL | / let tmp = v[i1 + i2]; +LL | | v[i1 + i2] = v[i2]; +LL | | v[i2] = tmp; + | |________________^ help: try: `v.swap(i1 + i2, i2);` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed b/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed index c8456805ee6ed..a0b707628a88e 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed @@ -17,3 +17,48 @@ fn main() { let x: Option> = None; x.unwrap_or_default(); } + +// Issue #12531 +unsafe fn no_deref_ptr(a: Option, b: *const Option) -> i32 { + match a { + // `*b` being correct depends on `a == Some(_)` + Some(_) => (*b).unwrap_or_default(), + _ => 0, + } +} + +const fn issue_12568(opt: Option) -> bool { + match opt { + Some(s) => s, + None => false, + } +} + +fn issue_12569() { + let match_none_se = match 1u32.checked_div(0) { + Some(v) => v, + None => { + println!("important"); + 0 + }, + }; + let match_some_se = match 1u32.checked_div(0) { + Some(v) => { + println!("important"); + v + }, + None => 0, + }; + let iflet_else_se = if let Some(v) = 1u32.checked_div(0) { + v + } else { + println!("important"); + 0 + }; + let iflet_then_se = if let Some(v) = 1u32.checked_div(0) { + println!("important"); + v + } else { + 0 + }; +} diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs b/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs index 820717be53a88..1d4cca12f6c78 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs @@ -38,3 +38,51 @@ fn main() { Vec::default() }; } + +// Issue #12531 +unsafe fn no_deref_ptr(a: Option, b: *const Option) -> i32 { + match a { + // `*b` being correct depends on `a == Some(_)` + Some(_) => match *b { + Some(v) => v, + _ => 0, + }, + _ => 0, + } +} + +const fn issue_12568(opt: Option) -> bool { + match opt { + Some(s) => s, + None => false, + } +} + +fn issue_12569() { + let match_none_se = match 1u32.checked_div(0) { + Some(v) => v, + None => { + println!("important"); + 0 + }, + }; + let match_some_se = match 1u32.checked_div(0) { + Some(v) => { + println!("important"); + v + }, + None => 0, + }; + let iflet_else_se = if let Some(v) = 1u32.checked_div(0) { + v + } else { + println!("important"); + 0 + }; + let iflet_then_se = if let Some(v) = 1u32.checked_div(0) { + println!("important"); + v + } else { + 0 + }; +} diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr b/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr index f4eb658358842..d89212e60459e 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr @@ -52,5 +52,15 @@ LL | | Vec::default() LL | | }; | |_____^ help: replace it with: `x.unwrap_or_default()` -error: aborting due to 5 previous errors +error: match can be simplified with `.unwrap_or_default()` + --> tests/ui/manual_unwrap_or_default.rs:46:20 + | +LL | Some(_) => match *b { + | ____________________^ +LL | | Some(v) => v, +LL | | _ => 0, +LL | | }, + | |_________^ help: replace it with: `(*b).unwrap_or_default()` + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/map_clone.fixed b/src/tools/clippy/tests/ui/map_clone.fixed index e58b6b2f19ea3..f9f8dc1a51232 100644 --- a/src/tools/clippy/tests/ui/map_clone.fixed +++ b/src/tools/clippy/tests/ui/map_clone.fixed @@ -131,4 +131,28 @@ fn main() { let x: Vec<&u8> = vec![]; let y = x.into_iter().map(|x| u8::clone(loop {})); } + + // Issue #12528 + { + // Don't lint these + use std::rc::{Rc, Weak as RcWeak}; + use std::sync::{Arc, Weak as ArcWeak}; + struct Foo; + + let x = Arc::new(Foo); + let y = Some(&x); + let _z = y.map(Arc::clone); + + let x = Rc::new(Foo); + let y = Some(&x); + let _z = y.map(Rc::clone); + + let x = Arc::downgrade(&Arc::new(Foo)); + let y = Some(&x); + let _z = y.map(ArcWeak::clone); + + let x = Rc::downgrade(&Rc::new(Foo)); + let y = Some(&x); + let _z = y.map(RcWeak::clone); + } } diff --git a/src/tools/clippy/tests/ui/map_clone.rs b/src/tools/clippy/tests/ui/map_clone.rs index e642e4046f8b7..a5c19ce063191 100644 --- a/src/tools/clippy/tests/ui/map_clone.rs +++ b/src/tools/clippy/tests/ui/map_clone.rs @@ -131,4 +131,28 @@ fn main() { let x: Vec<&u8> = vec![]; let y = x.into_iter().map(|x| u8::clone(loop {})); } + + // Issue #12528 + { + // Don't lint these + use std::rc::{Rc, Weak as RcWeak}; + use std::sync::{Arc, Weak as ArcWeak}; + struct Foo; + + let x = Arc::new(Foo); + let y = Some(&x); + let _z = y.map(Arc::clone); + + let x = Rc::new(Foo); + let y = Some(&x); + let _z = y.map(Rc::clone); + + let x = Arc::downgrade(&Arc::new(Foo)); + let y = Some(&x); + let _z = y.map(ArcWeak::clone); + + let x = Rc::downgrade(&Rc::new(Foo)); + let y = Some(&x); + let _z = y.map(RcWeak::clone); + } } diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs index 6985c2d0c1956..12a8320c8f329 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs @@ -1,5 +1,5 @@ #![warn(clippy::missing_const_for_fn)] -#![allow(incomplete_features, clippy::let_and_return)] +#![allow(incomplete_features, clippy::let_and_return, clippy::missing_transmute_annotations)] #![feature(const_mut_refs)] #![feature(const_trait_impl)] diff --git a/src/tools/clippy/tests/ui/missing_transmute_annotations.fixed b/src/tools/clippy/tests/ui/missing_transmute_annotations.fixed new file mode 100644 index 0000000000000..a3c94ab139ec6 --- /dev/null +++ b/src/tools/clippy/tests/ui/missing_transmute_annotations.fixed @@ -0,0 +1,78 @@ +//@aux-build:macro_rules.rs + +#![warn(clippy::missing_transmute_annotations)] +#![allow(clippy::let_with_type_underscore)] + +#[macro_use] +extern crate macro_rules; + +macro_rules! local_bad_transmute { + ($e:expr) => { + std::mem::transmute::<[u16; 2], i32>($e) + //~^ ERROR: transmute used without annotations + }; +} + +fn bar(x: i32) -> i32 { + x +} + +unsafe fn foo1() -> i32 { + // Should not warn! + std::mem::transmute([1u16, 2u16]) +} + +// Should not warn! +const _: i32 = unsafe { std::mem::transmute([1u16, 2u16]) }; + +#[repr(i32)] +enum Foo { + A = 0, +} + +unsafe fn foo2() -> i32 { + let mut i: i32 = 0; + i = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations + i = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations + i = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations + i = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations + + let x: i32 = bar(std::mem::transmute::<[u16; 2], i32>([1u16, 2u16])); + //~^ ERROR: transmute used without annotations + bar(std::mem::transmute::<[u16; 2], i32>([1u16, 2u16])); + //~^ ERROR: transmute used without annotations + + i = local_bad_transmute!([1u16, 2u16]); + + // Should not warn. + i = bad_transmute!([1u16, 2u16]); + + i = std::mem::transmute::<[i16; 2], i32>([0i16, 0i16]); + //~^ ERROR: transmute used without annotations + + i = std::mem::transmute::(Foo::A); + //~^ ERROR: transmute used without annotations + + i +} + +fn main() { + let x: _ = unsafe { std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]) }; + //~^ ERROR: transmute used without annotations + unsafe { + let x: _ = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations + + // Should not warn. + std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); + let x = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); + let x: i32 = std::mem::transmute::<[u16; 2], _>([1u16, 2u16]); + let x: i32 = std::mem::transmute::<_, i32>([1u16, 2u16]); + let x: i32 = std::mem::transmute([1u16, 2u16]); + } + let x: i32 = unsafe { std::mem::transmute([1u16, 2u16]) }; +} diff --git a/src/tools/clippy/tests/ui/missing_transmute_annotations.rs b/src/tools/clippy/tests/ui/missing_transmute_annotations.rs new file mode 100644 index 0000000000000..c12e1b0f8d220 --- /dev/null +++ b/src/tools/clippy/tests/ui/missing_transmute_annotations.rs @@ -0,0 +1,78 @@ +//@aux-build:macro_rules.rs + +#![warn(clippy::missing_transmute_annotations)] +#![allow(clippy::let_with_type_underscore)] + +#[macro_use] +extern crate macro_rules; + +macro_rules! local_bad_transmute { + ($e:expr) => { + std::mem::transmute($e) + //~^ ERROR: transmute used without annotations + }; +} + +fn bar(x: i32) -> i32 { + x +} + +unsafe fn foo1() -> i32 { + // Should not warn! + std::mem::transmute([1u16, 2u16]) +} + +// Should not warn! +const _: i32 = unsafe { std::mem::transmute([1u16, 2u16]) }; + +#[repr(i32)] +enum Foo { + A = 0, +} + +unsafe fn foo2() -> i32 { + let mut i: i32 = 0; + i = std::mem::transmute([1u16, 2u16]); + //~^ ERROR: transmute used without annotations + i = std::mem::transmute::<_, _>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations + i = std::mem::transmute::<_, i32>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations + i = std::mem::transmute::<[u16; 2], _>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations + + let x: i32 = bar(std::mem::transmute::<[u16; 2], _>([1u16, 2u16])); + //~^ ERROR: transmute used without annotations + bar(std::mem::transmute::<[u16; 2], _>([1u16, 2u16])); + //~^ ERROR: transmute used without annotations + + i = local_bad_transmute!([1u16, 2u16]); + + // Should not warn. + i = bad_transmute!([1u16, 2u16]); + + i = std::mem::transmute([0i16, 0i16]); + //~^ ERROR: transmute used without annotations + + i = std::mem::transmute(Foo::A); + //~^ ERROR: transmute used without annotations + + i +} + +fn main() { + let x: _ = unsafe { std::mem::transmute::<_, i32>([1u16, 2u16]) }; + //~^ ERROR: transmute used without annotations + unsafe { + let x: _ = std::mem::transmute::<_, i32>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations + + // Should not warn. + std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); + let x = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); + let x: i32 = std::mem::transmute::<[u16; 2], _>([1u16, 2u16]); + let x: i32 = std::mem::transmute::<_, i32>([1u16, 2u16]); + let x: i32 = std::mem::transmute([1u16, 2u16]); + } + let x: i32 = unsafe { std::mem::transmute([1u16, 2u16]) }; +} diff --git a/src/tools/clippy/tests/ui/missing_transmute_annotations.stderr b/src/tools/clippy/tests/ui/missing_transmute_annotations.stderr new file mode 100644 index 0000000000000..5903ed488ef18 --- /dev/null +++ b/src/tools/clippy/tests/ui/missing_transmute_annotations.stderr @@ -0,0 +1,76 @@ +error: transmute used without annotations + --> tests/ui/missing_transmute_annotations.rs:35:19 + | +LL | i = std::mem::transmute([1u16, 2u16]); + | ^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` + | + = note: `-D clippy::missing-transmute-annotations` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::missing_transmute_annotations)]` + +error: transmute used without annotations + --> tests/ui/missing_transmute_annotations.rs:37:19 + | +LL | i = std::mem::transmute::<_, _>([1u16, 2u16]); + | ^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` + +error: transmute used without annotations + --> tests/ui/missing_transmute_annotations.rs:39:19 + | +LL | i = std::mem::transmute::<_, i32>([1u16, 2u16]); + | ^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` + +error: transmute used without annotations + --> tests/ui/missing_transmute_annotations.rs:41:19 + | +LL | i = std::mem::transmute::<[u16; 2], _>([1u16, 2u16]); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` + +error: transmute used without annotations + --> tests/ui/missing_transmute_annotations.rs:44:32 + | +LL | let x: i32 = bar(std::mem::transmute::<[u16; 2], _>([1u16, 2u16])); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` + +error: transmute used without annotations + --> tests/ui/missing_transmute_annotations.rs:46:19 + | +LL | bar(std::mem::transmute::<[u16; 2], _>([1u16, 2u16])); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` + +error: transmute used without annotations + --> tests/ui/missing_transmute_annotations.rs:11:19 + | +LL | std::mem::transmute($e) + | ^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` +... +LL | i = local_bad_transmute!([1u16, 2u16]); + | ---------------------------------- in this macro invocation + | + = note: this error originates in the macro `local_bad_transmute` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: transmute used without annotations + --> tests/ui/missing_transmute_annotations.rs:54:19 + | +LL | i = std::mem::transmute([0i16, 0i16]); + | ^^^^^^^^^ help: consider adding missing annotations: `transmute::<[i16; 2], i32>` + +error: transmute used without annotations + --> tests/ui/missing_transmute_annotations.rs:57:19 + | +LL | i = std::mem::transmute(Foo::A); + | ^^^^^^^^^ help: consider adding missing annotations: `transmute::` + +error: transmute used without annotations + --> tests/ui/missing_transmute_annotations.rs:64:35 + | +LL | let x: _ = unsafe { std::mem::transmute::<_, i32>([1u16, 2u16]) }; + | ^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` + +error: transmute used without annotations + --> tests/ui/missing_transmute_annotations.rs:67:30 + | +LL | let x: _ = std::mem::transmute::<_, i32>([1u16, 2u16]); + | ^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` + +error: aborting due to 11 previous errors + diff --git a/src/tools/clippy/tests/ui/mixed_attributes_style.rs b/src/tools/clippy/tests/ui/mixed_attributes_style.rs index 4f89aa8a5e552..1a646c2652236 100644 --- a/src/tools/clippy/tests/ui/mixed_attributes_style.rs +++ b/src/tools/clippy/tests/ui/mixed_attributes_style.rs @@ -1,6 +1,12 @@ +//@aux-build:proc_macro_attr.rs +//@compile-flags: --test --cfg dummy_cfg +#![feature(custom_inner_attributes)] #![warn(clippy::mixed_attributes_style)] #![allow(clippy::duplicated_attributes)] +#[macro_use] +extern crate proc_macro_attr; + #[allow(unused)] //~ ERROR: item has both inner and outer attributes fn foo1() { #![allow(unused)] @@ -38,3 +44,57 @@ mod bar { fn main() { // test code goes here } + +// issue #12435 +#[cfg(test)] +mod tests { + //! Module doc, don't lint +} +#[allow(unused)] +mod baz { + //! Module doc, don't lint + const FOO: u8 = 0; +} +/// Module doc, don't lint +mod quz { + #![allow(unused)] +} + +mod issue_12530 { + // don't lint different attributes entirely + #[cfg(test)] + mod tests { + #![allow(clippy::unreadable_literal)] + + #[allow(dead_code)] //~ ERROR: item has both inner and outer attributes + mod inner_mod { + #![allow(dead_code)] + } + } + #[cfg(dummy_cfg)] + mod another_mod { + #![allow(clippy::question_mark)] + } + /// Nested mod + mod nested_mod { + #[allow(dead_code)] //~ ERROR: item has both inner and outer attributes + mod inner_mod { + #![allow(dead_code)] + } + } + /// Nested mod //~ ERROR: item has both inner and outer attributes + #[allow(unused)] + mod nest_mod_2 { + #![allow(unused)] + + #[allow(dead_code)] //~ ERROR: item has both inner and outer attributes + mod inner_mod { + #![allow(dead_code)] + } + } + // Different path symbols - Known FN + #[dummy] + fn use_dummy() { + #![proc_macro_attr::dummy] + } +} diff --git a/src/tools/clippy/tests/ui/mixed_attributes_style.stderr b/src/tools/clippy/tests/ui/mixed_attributes_style.stderr index ed798073cb7c7..a1d3fc430f6cf 100644 --- a/src/tools/clippy/tests/ui/mixed_attributes_style.stderr +++ b/src/tools/clippy/tests/ui/mixed_attributes_style.stderr @@ -1,5 +1,5 @@ error: item has both inner and outer attributes - --> tests/ui/mixed_attributes_style.rs:4:1 + --> tests/ui/mixed_attributes_style.rs:10:1 | LL | / #[allow(unused)] LL | | fn foo1() { @@ -10,7 +10,7 @@ LL | | #![allow(unused)] = help: to override `-D warnings` add `#[allow(clippy::mixed_attributes_style)]` error: item has both inner and outer attributes - --> tests/ui/mixed_attributes_style.rs:18:1 + --> tests/ui/mixed_attributes_style.rs:24:1 | LL | / /// linux LL | | @@ -19,12 +19,45 @@ LL | | //! windows | |_______________^ error: item has both inner and outer attributes - --> tests/ui/mixed_attributes_style.rs:33:1 + --> tests/ui/mixed_attributes_style.rs:39:1 | LL | / #[allow(unused)] LL | | mod bar { LL | | #![allow(unused)] | |_____________________^ -error: aborting due to 3 previous errors +error: item has both inner and outer attributes + --> tests/ui/mixed_attributes_style.rs:69:9 + | +LL | / #[allow(dead_code)] +LL | | mod inner_mod { +LL | | #![allow(dead_code)] + | |________________________________^ + +error: item has both inner and outer attributes + --> tests/ui/mixed_attributes_style.rs:80:9 + | +LL | / #[allow(dead_code)] +LL | | mod inner_mod { +LL | | #![allow(dead_code)] + | |________________________________^ + +error: item has both inner and outer attributes + --> tests/ui/mixed_attributes_style.rs:85:5 + | +LL | / /// Nested mod +LL | | #[allow(unused)] +LL | | mod nest_mod_2 { +LL | | #![allow(unused)] + | |_________________________^ + +error: item has both inner and outer attributes + --> tests/ui/mixed_attributes_style.rs:90:9 + | +LL | / #[allow(dead_code)] +LL | | mod inner_mod { +LL | | #![allow(dead_code)] + | |________________________________^ + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/mixed_attributes_style/auxiliary/submodule.rs b/src/tools/clippy/tests/ui/mixed_attributes_style/auxiliary/submodule.rs new file mode 100644 index 0000000000000..df44b07a69414 --- /dev/null +++ b/src/tools/clippy/tests/ui/mixed_attributes_style/auxiliary/submodule.rs @@ -0,0 +1,9 @@ +//! Module level doc + +#![allow(dead_code)] + +#[allow(unused)] +//~^ ERROR: item has both inner and outer attributes +mod foo { + #![allow(dead_code)] +} diff --git a/src/tools/clippy/tests/ui/mixed_attributes_style/global_allow.rs b/src/tools/clippy/tests/ui/mixed_attributes_style/global_allow.rs new file mode 100644 index 0000000000000..153262e65570d --- /dev/null +++ b/src/tools/clippy/tests/ui/mixed_attributes_style/global_allow.rs @@ -0,0 +1,7 @@ +// issue 12436 +#![allow(clippy::mixed_attributes_style)] + +#[path = "auxiliary/submodule.rs"] +mod submodule; + +fn main() {} diff --git a/src/tools/clippy/tests/ui/mixed_attributes_style/mod_declaration.rs b/src/tools/clippy/tests/ui/mixed_attributes_style/mod_declaration.rs new file mode 100644 index 0000000000000..b0f1f0bda9e60 --- /dev/null +++ b/src/tools/clippy/tests/ui/mixed_attributes_style/mod_declaration.rs @@ -0,0 +1,3 @@ +#[path = "auxiliary/submodule.rs"] // don't lint. +/// This doc comment should not lint, it could be used to add context to the original module doc +mod submodule; diff --git a/src/tools/clippy/tests/ui/mixed_attributes_style/mod_declaration.stderr b/src/tools/clippy/tests/ui/mixed_attributes_style/mod_declaration.stderr new file mode 100644 index 0000000000000..968c537c7e44f --- /dev/null +++ b/src/tools/clippy/tests/ui/mixed_attributes_style/mod_declaration.stderr @@ -0,0 +1,14 @@ +error: item has both inner and outer attributes + --> tests/ui/mixed_attributes_style/auxiliary/submodule.rs:5:1 + | +LL | / #[allow(unused)] +LL | | +LL | | mod foo { +LL | | #![allow(dead_code)] + | |________________________^ + | + = note: `-D clippy::mixed-attributes-style` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::mixed_attributes_style)]` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.fixed b/src/tools/clippy/tests/ui/ptr_cast_constness.fixed index c410a660dc42f..33c0725faad01 100644 --- a/src/tools/clippy/tests/ui/ptr_cast_constness.fixed +++ b/src/tools/clippy/tests/ui/ptr_cast_constness.fixed @@ -1,7 +1,12 @@ //@aux-build:proc_macros.rs #![warn(clippy::ptr_cast_constness)] -#![allow(clippy::transmute_ptr_to_ref, clippy::unnecessary_cast, unused)] +#![allow( + clippy::transmute_ptr_to_ref, + clippy::unnecessary_cast, + unused, + clippy::missing_transmute_annotations +)] extern crate proc_macros; use proc_macros::{external, inline_macros}; diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.rs b/src/tools/clippy/tests/ui/ptr_cast_constness.rs index 6025b857b8f4b..24d959856dbd5 100644 --- a/src/tools/clippy/tests/ui/ptr_cast_constness.rs +++ b/src/tools/clippy/tests/ui/ptr_cast_constness.rs @@ -1,7 +1,12 @@ //@aux-build:proc_macros.rs #![warn(clippy::ptr_cast_constness)] -#![allow(clippy::transmute_ptr_to_ref, clippy::unnecessary_cast, unused)] +#![allow( + clippy::transmute_ptr_to_ref, + clippy::unnecessary_cast, + unused, + clippy::missing_transmute_annotations +)] extern crate proc_macros; use proc_macros::{external, inline_macros}; diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.stderr b/src/tools/clippy/tests/ui/ptr_cast_constness.stderr index 8e2bec527ffb6..322c3585e62fe 100644 --- a/src/tools/clippy/tests/ui/ptr_cast_constness.stderr +++ b/src/tools/clippy/tests/ui/ptr_cast_constness.stderr @@ -1,5 +1,5 @@ error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:10:41 + --> tests/ui/ptr_cast_constness.rs:15:41 | LL | let _: &mut T = std::mem::transmute(p as *mut T); | ^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `p.cast_mut()` @@ -8,37 +8,37 @@ LL | let _: &mut T = std::mem::transmute(p as *mut T); = help: to override `-D warnings` add `#[allow(clippy::ptr_cast_constness)]` error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:11:19 + --> tests/ui/ptr_cast_constness.rs:16:19 | LL | let _ = &mut *(p as *mut T); | ^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `p.cast_mut()` error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:26:17 + --> tests/ui/ptr_cast_constness.rs:31:17 | LL | let _ = *ptr_ptr as *mut u32; | ^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `(*ptr_ptr).cast_mut()` error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:29:13 + --> tests/ui/ptr_cast_constness.rs:34:13 | LL | let _ = ptr as *mut u32; | ^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `ptr.cast_mut()` error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:30:13 + --> tests/ui/ptr_cast_constness.rs:35:13 | LL | let _ = mut_ptr as *const u32; | ^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `mut_ptr.cast_const()` error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:59:13 + --> tests/ui/ptr_cast_constness.rs:64:13 | LL | let _ = ptr as *mut u32; | ^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `ptr.cast_mut()` error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:60:13 + --> tests/ui/ptr_cast_constness.rs:65:13 | LL | let _ = mut_ptr as *const u32; | ^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `mut_ptr.cast_const()` diff --git a/src/tools/clippy/tests/ui/question_mark.fixed b/src/tools/clippy/tests/ui/question_mark.fixed index 567472a8af22d..679388372e618 100644 --- a/src/tools/clippy/tests/ui/question_mark.fixed +++ b/src/tools/clippy/tests/ui/question_mark.fixed @@ -283,3 +283,37 @@ fn issue12337() -> Option { }; Some(42) } + +fn issue11983(option: &Option) -> Option<()> { + // Don't lint, `&Option` dose not impl `Try`. + let Some(v) = option else { return None }; + + let opt = Some(String::new()); + // Don't lint, `branch` method in `Try` takes ownership of `opt`, + // and `(&opt)?` also doesn't work since it's `&Option`. + let Some(v) = &opt else { return None }; + let mov = opt; + + Some(()) +} + +struct Foo { + owned: Option, +} +struct Bar { + foo: Foo, +} +#[allow(clippy::disallowed_names)] +fn issue12412(foo: &Foo, bar: &Bar) -> Option<()> { + // Don't lint, `owned` is behind a shared reference. + let Some(v) = &foo.owned else { + return None; + }; + // Don't lint, `owned` is behind a shared reference. + let Some(v) = &bar.foo.owned else { + return None; + }; + // lint + let v = bar.foo.owned.clone()?; + Some(()) +} diff --git a/src/tools/clippy/tests/ui/question_mark.rs b/src/tools/clippy/tests/ui/question_mark.rs index abf8c270de83d..601ab78bf5aab 100644 --- a/src/tools/clippy/tests/ui/question_mark.rs +++ b/src/tools/clippy/tests/ui/question_mark.rs @@ -323,3 +323,39 @@ fn issue12337() -> Option { }; Some(42) } + +fn issue11983(option: &Option) -> Option<()> { + // Don't lint, `&Option` dose not impl `Try`. + let Some(v) = option else { return None }; + + let opt = Some(String::new()); + // Don't lint, `branch` method in `Try` takes ownership of `opt`, + // and `(&opt)?` also doesn't work since it's `&Option`. + let Some(v) = &opt else { return None }; + let mov = opt; + + Some(()) +} + +struct Foo { + owned: Option, +} +struct Bar { + foo: Foo, +} +#[allow(clippy::disallowed_names)] +fn issue12412(foo: &Foo, bar: &Bar) -> Option<()> { + // Don't lint, `owned` is behind a shared reference. + let Some(v) = &foo.owned else { + return None; + }; + // Don't lint, `owned` is behind a shared reference. + let Some(v) = &bar.foo.owned else { + return None; + }; + // lint + let Some(v) = bar.foo.owned.clone() else { + return None; + }; + Some(()) +} diff --git a/src/tools/clippy/tests/ui/question_mark.stderr b/src/tools/clippy/tests/ui/question_mark.stderr index 4fcccdf5512fc..5f26a7ea2c3e3 100644 --- a/src/tools/clippy/tests/ui/question_mark.stderr +++ b/src/tools/clippy/tests/ui/question_mark.stderr @@ -141,5 +141,13 @@ LL | | // https://github.com/rust-lang/rust-clippy/pull/11001#is LL | | } | |_____________^ help: replace it with: `a?;` -error: aborting due to 16 previous errors +error: this `let...else` may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:357:5 + | +LL | / let Some(v) = bar.foo.owned.clone() else { +LL | | return None; +LL | | }; + | |______^ help: replace it with: `let v = bar.foo.owned.clone()?;` + +error: aborting due to 17 previous errors diff --git a/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.rs b/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.rs index 1bd4cd5fb50de..07280351e76ca 100644 --- a/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.rs +++ b/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.rs @@ -1,3 +1,4 @@ +#![allow(clippy::legacy_numeric_constants)] #![warn(clippy::suspicious_arithmetic_impl)] use std::ops::{ Add, AddAssign, BitAnd, BitOr, BitOrAssign, BitXor, Div, DivAssign, Mul, MulAssign, Rem, Shl, Shr, Sub, diff --git a/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.stderr b/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.stderr index 193cd64149b1f..1bfca49a635d4 100644 --- a/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.stderr +++ b/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.stderr @@ -1,5 +1,5 @@ error: suspicious use of `-` in `Add` impl - --> tests/ui/suspicious_arithmetic_impl.rs:13:20 + --> tests/ui/suspicious_arithmetic_impl.rs:14:20 | LL | Foo(self.0 - other.0) | ^ @@ -8,7 +8,7 @@ LL | Foo(self.0 - other.0) = help: to override `-D warnings` add `#[allow(clippy::suspicious_arithmetic_impl)]` error: suspicious use of `-` in `AddAssign` impl - --> tests/ui/suspicious_arithmetic_impl.rs:21:23 + --> tests/ui/suspicious_arithmetic_impl.rs:22:23 | LL | *self = *self - other; | ^ @@ -17,43 +17,43 @@ LL | *self = *self - other; = help: to override `-D warnings` add `#[allow(clippy::suspicious_op_assign_impl)]` error: suspicious use of `/` in `MulAssign` impl - --> tests/ui/suspicious_arithmetic_impl.rs:36:16 + --> tests/ui/suspicious_arithmetic_impl.rs:37:16 | LL | self.0 /= other.0; | ^^ error: suspicious use of `/` in `Rem` impl - --> tests/ui/suspicious_arithmetic_impl.rs:75:20 + --> tests/ui/suspicious_arithmetic_impl.rs:76:20 | LL | Foo(self.0 / other.0) | ^ error: suspicious use of `|` in `BitAnd` impl - --> tests/ui/suspicious_arithmetic_impl.rs:84:20 + --> tests/ui/suspicious_arithmetic_impl.rs:85:20 | LL | Foo(self.0 | other.0) | ^ error: suspicious use of `^` in `BitOr` impl - --> tests/ui/suspicious_arithmetic_impl.rs:93:20 + --> tests/ui/suspicious_arithmetic_impl.rs:94:20 | LL | Foo(self.0 ^ other.0) | ^ error: suspicious use of `&` in `BitXor` impl - --> tests/ui/suspicious_arithmetic_impl.rs:102:20 + --> tests/ui/suspicious_arithmetic_impl.rs:103:20 | LL | Foo(self.0 & other.0) | ^ error: suspicious use of `>>` in `Shl` impl - --> tests/ui/suspicious_arithmetic_impl.rs:111:20 + --> tests/ui/suspicious_arithmetic_impl.rs:112:20 | LL | Foo(self.0 >> other.0) | ^^ error: suspicious use of `<<` in `Shr` impl - --> tests/ui/suspicious_arithmetic_impl.rs:120:20 + --> tests/ui/suspicious_arithmetic_impl.rs:121:20 | LL | Foo(self.0 << other.0) | ^^ diff --git a/src/tools/clippy/tests/ui/suspicious_else_formatting.rs b/src/tools/clippy/tests/ui/suspicious_else_formatting.rs index c0856427eaef7..3d5c892eb606e 100644 --- a/src/tools/clippy/tests/ui/suspicious_else_formatting.rs +++ b/src/tools/clippy/tests/ui/suspicious_else_formatting.rs @@ -120,6 +120,34 @@ fn main() { /* whelp */ { } + + // #12497 Don't trigger lint as rustfmt wants it + if true { + println!("true"); + } + /*else if false { +}*/ + else { + println!("false"); + } + + if true { + println!("true"); + } // else if false {} + else { + println!("false"); + } + + if true { + println!("true"); + } /* if true { + println!("true"); +} + */ + else { + println!("false"); + } + } // #7650 - Don't lint. Proc-macro using bad spans for `if` expressions. diff --git a/src/tools/clippy/tests/ui/transmute.rs b/src/tools/clippy/tests/ui/transmute.rs index 1796ccaf28e1a..be6e071767d98 100644 --- a/src/tools/clippy/tests/ui/transmute.rs +++ b/src/tools/clippy/tests/ui/transmute.rs @@ -1,4 +1,9 @@ -#![allow(dead_code, clippy::borrow_as_ptr, clippy::needless_lifetimes)] +#![allow( + dead_code, + clippy::borrow_as_ptr, + clippy::needless_lifetimes, + clippy::missing_transmute_annotations +)] //@no-rustfix extern crate core; diff --git a/src/tools/clippy/tests/ui/transmute.stderr b/src/tools/clippy/tests/ui/transmute.stderr index 3ed6cb2b3f974..375e8f19dd6b4 100644 --- a/src/tools/clippy/tests/ui/transmute.stderr +++ b/src/tools/clippy/tests/ui/transmute.stderr @@ -1,5 +1,5 @@ error: transmute from a reference to a pointer - --> tests/ui/transmute.rs:24:23 + --> tests/ui/transmute.rs:29:23 | LL | let _: *const T = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T` @@ -8,61 +8,61 @@ LL | let _: *const T = core::intrinsics::transmute(t); = help: to override `-D warnings` add `#[allow(clippy::useless_transmute)]` error: transmute from a reference to a pointer - --> tests/ui/transmute.rs:28:21 + --> tests/ui/transmute.rs:33:21 | LL | let _: *mut T = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T` error: transmute from a reference to a pointer - --> tests/ui/transmute.rs:31:23 + --> tests/ui/transmute.rs:36:23 | LL | let _: *const U = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U` error: transmute from a type (`std::vec::Vec`) to itself - --> tests/ui/transmute.rs:38:27 + --> tests/ui/transmute.rs:43:27 | LL | let _: Vec = core::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> tests/ui/transmute.rs:41:27 + --> tests/ui/transmute.rs:46:27 | LL | let _: Vec = core::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> tests/ui/transmute.rs:44:27 + --> tests/ui/transmute.rs:49:27 | LL | let _: Vec = std::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> tests/ui/transmute.rs:47:27 + --> tests/ui/transmute.rs:52:27 | LL | let _: Vec = std::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> tests/ui/transmute.rs:50:27 + --> tests/ui/transmute.rs:55:27 | LL | let _: Vec = my_transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^ error: transmute from an integer to a pointer - --> tests/ui/transmute.rs:53:31 + --> tests/ui/transmute.rs:58:31 | LL | let _: *const usize = std::mem::transmute(5_isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `5_isize as *const usize` error: transmute from an integer to a pointer - --> tests/ui/transmute.rs:58:31 + --> tests/ui/transmute.rs:63:31 | LL | let _: *const usize = std::mem::transmute(1 + 1usize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(1 + 1usize) as *const usize` error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`) - --> tests/ui/transmute.rs:90:24 + --> tests/ui/transmute.rs:95:24 | LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -71,25 +71,25 @@ LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); = help: to override `-D warnings` add `#[allow(clippy::crosspointer_transmute)]` error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`) - --> tests/ui/transmute.rs:94:24 + --> tests/ui/transmute.rs:99:24 | LL | let _: Usize = core::intrinsics::transmute(int_mut_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`) - --> tests/ui/transmute.rs:97:31 + --> tests/ui/transmute.rs:102:31 | LL | let _: *const Usize = core::intrinsics::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`) - --> tests/ui/transmute.rs:100:29 + --> tests/ui/transmute.rs:105:29 | LL | let _: *mut Usize = core::intrinsics::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a `u8` to a `bool` - --> tests/ui/transmute.rs:107:28 + --> tests/ui/transmute.rs:112:28 | LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0` @@ -98,7 +98,7 @@ LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_bool)]` error: transmute from a `u32` to a `f32` - --> tests/ui/transmute.rs:115:31 + --> tests/ui/transmute.rs:120:31 | LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` @@ -107,25 +107,25 @@ LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_float)]` error: transmute from a `i32` to a `f32` - --> tests/ui/transmute.rs:118:31 + --> tests/ui/transmute.rs:123:31 | LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` error: transmute from a `u64` to a `f64` - --> tests/ui/transmute.rs:120:31 + --> tests/ui/transmute.rs:125:31 | LL | let _: f64 = unsafe { std::mem::transmute(0_u64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_u64)` error: transmute from a `i64` to a `f64` - --> tests/ui/transmute.rs:122:31 + --> tests/ui/transmute.rs:127:31 | LL | let _: f64 = unsafe { std::mem::transmute(0_i64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` error: transmute from a `u8` to a `[u8; 1]` - --> tests/ui/transmute.rs:143:30 + --> tests/ui/transmute.rs:148:30 | LL | let _: [u8; 1] = std::mem::transmute(0u8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` @@ -134,85 +134,85 @@ LL | let _: [u8; 1] = std::mem::transmute(0u8); = help: to override `-D warnings` add `#[allow(clippy::transmute_num_to_bytes)]` error: transmute from a `u32` to a `[u8; 4]` - --> tests/ui/transmute.rs:146:30 + --> tests/ui/transmute.rs:151:30 | LL | let _: [u8; 4] = std::mem::transmute(0u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` error: transmute from a `u128` to a `[u8; 16]` - --> tests/ui/transmute.rs:148:31 + --> tests/ui/transmute.rs:153:31 | LL | let _: [u8; 16] = std::mem::transmute(0u128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` error: transmute from a `i8` to a `[u8; 1]` - --> tests/ui/transmute.rs:150:30 + --> tests/ui/transmute.rs:155:30 | LL | let _: [u8; 1] = std::mem::transmute(0i8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` error: transmute from a `i32` to a `[u8; 4]` - --> tests/ui/transmute.rs:152:30 + --> tests/ui/transmute.rs:157:30 | LL | let _: [u8; 4] = std::mem::transmute(0i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` error: transmute from a `i128` to a `[u8; 16]` - --> tests/ui/transmute.rs:154:31 + --> tests/ui/transmute.rs:159:31 | LL | let _: [u8; 16] = std::mem::transmute(0i128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` error: transmute from a `f32` to a `[u8; 4]` - --> tests/ui/transmute.rs:156:30 + --> tests/ui/transmute.rs:161:30 | LL | let _: [u8; 4] = std::mem::transmute(0.0f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()` error: transmute from a `f64` to a `[u8; 8]` - --> tests/ui/transmute.rs:158:30 + --> tests/ui/transmute.rs:163:30 | LL | let _: [u8; 8] = std::mem::transmute(0.0f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()` error: transmute from a `u8` to a `[u8; 1]` - --> tests/ui/transmute.rs:164:30 + --> tests/ui/transmute.rs:169:30 | LL | let _: [u8; 1] = std::mem::transmute(0u8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` error: transmute from a `u32` to a `[u8; 4]` - --> tests/ui/transmute.rs:166:30 + --> tests/ui/transmute.rs:171:30 | LL | let _: [u8; 4] = std::mem::transmute(0u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` error: transmute from a `u128` to a `[u8; 16]` - --> tests/ui/transmute.rs:168:31 + --> tests/ui/transmute.rs:173:31 | LL | let _: [u8; 16] = std::mem::transmute(0u128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` error: transmute from a `i8` to a `[u8; 1]` - --> tests/ui/transmute.rs:170:30 + --> tests/ui/transmute.rs:175:30 | LL | let _: [u8; 1] = std::mem::transmute(0i8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` error: transmute from a `i32` to a `[u8; 4]` - --> tests/ui/transmute.rs:172:30 + --> tests/ui/transmute.rs:177:30 | LL | let _: [u8; 4] = std::mem::transmute(0i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` error: transmute from a `i128` to a `[u8; 16]` - --> tests/ui/transmute.rs:174:31 + --> tests/ui/transmute.rs:179:31 | LL | let _: [u8; 16] = std::mem::transmute(0i128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` error: transmute from a `&[u8]` to a `&str` - --> tests/ui/transmute.rs:185:28 + --> tests/ui/transmute.rs:190:28 | LL | let _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(B).unwrap()` @@ -221,13 +221,13 @@ LL | let _: &str = unsafe { std::mem::transmute(B) }; = help: to override `-D warnings` add `#[allow(clippy::transmute_bytes_to_str)]` error: transmute from a `&mut [u8]` to a `&mut str` - --> tests/ui/transmute.rs:188:32 + --> tests/ui/transmute.rs:193:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` error: transmute from a `&[u8]` to a `&str` - --> tests/ui/transmute.rs:190:30 + --> tests/ui/transmute.rs:195:30 | LL | const _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_unchecked(B)` diff --git a/src/tools/clippy/tests/ui/transmute_collection.rs b/src/tools/clippy/tests/ui/transmute_collection.rs index 8bf454573355f..e30b34a5d7d6f 100644 --- a/src/tools/clippy/tests/ui/transmute_collection.rs +++ b/src/tools/clippy/tests/ui/transmute_collection.rs @@ -1,4 +1,5 @@ #![warn(clippy::unsound_collection_transmute)] +#![allow(clippy::missing_transmute_annotations)] use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque}; use std::mem::{transmute, MaybeUninit}; diff --git a/src/tools/clippy/tests/ui/transmute_collection.stderr b/src/tools/clippy/tests/ui/transmute_collection.stderr index f71fba6315ca7..06db9321064b4 100644 --- a/src/tools/clippy/tests/ui/transmute_collection.stderr +++ b/src/tools/clippy/tests/ui/transmute_collection.stderr @@ -1,5 +1,5 @@ error: transmute from `std::vec::Vec` to `std::vec::Vec` with mismatched layout is unsound - --> tests/ui/transmute_collection.rs:9:17 + --> tests/ui/transmute_collection.rs:10:17 | LL | let _ = transmute::<_, Vec>(vec![0u8]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,103 +8,103 @@ LL | let _ = transmute::<_, Vec>(vec![0u8]); = help: to override `-D warnings` add `#[allow(clippy::unsound_collection_transmute)]` error: transmute from `std::vec::Vec` to `std::vec::Vec<[u8; 4]>` with mismatched layout is unsound - --> tests/ui/transmute_collection.rs:13:17 + --> tests/ui/transmute_collection.rs:14:17 | LL | let _ = transmute::<_, Vec<[u8; 4]>>(vec![1234u32]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from `std::collections::VecDeque` to `std::collections::VecDeque` with mismatched layout is unsound - --> tests/ui/transmute_collection.rs:17:17 + --> tests/ui/transmute_collection.rs:18:17 | LL | let _ = transmute::<_, VecDeque>(VecDeque::::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from `std::collections::VecDeque<[u8; 4]>` to `std::collections::VecDeque` with mismatched layout is unsound - --> tests/ui/transmute_collection.rs:20:17 + --> tests/ui/transmute_collection.rs:21:17 | LL | let _ = transmute::<_, VecDeque>(VecDeque::<[u8; 4]>::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from `std::collections::BinaryHeap` to `std::collections::BinaryHeap` with mismatched layout is unsound - --> tests/ui/transmute_collection.rs:24:17 + --> tests/ui/transmute_collection.rs:25:17 | LL | let _ = transmute::<_, BinaryHeap>(BinaryHeap::::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from `std::collections::BinaryHeap<[u8; 4]>` to `std::collections::BinaryHeap` with mismatched layout is unsound - --> tests/ui/transmute_collection.rs:27:17 + --> tests/ui/transmute_collection.rs:28:17 | LL | let _ = transmute::<_, BinaryHeap>(BinaryHeap::<[u8; 4]>::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from `std::collections::BTreeSet` to `std::collections::BTreeSet` with mismatched layout is unsound - --> tests/ui/transmute_collection.rs:31:17 + --> tests/ui/transmute_collection.rs:32:17 | LL | let _ = transmute::<_, BTreeSet>(BTreeSet::::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from `std::collections::BTreeSet<[u8; 4]>` to `std::collections::BTreeSet` with mismatched layout is unsound - --> tests/ui/transmute_collection.rs:34:17 + --> tests/ui/transmute_collection.rs:35:17 | LL | let _ = transmute::<_, BTreeSet>(BTreeSet::<[u8; 4]>::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from `std::collections::HashSet` to `std::collections::HashSet` with mismatched layout is unsound - --> tests/ui/transmute_collection.rs:38:17 + --> tests/ui/transmute_collection.rs:39:17 | LL | let _ = transmute::<_, HashSet>(HashSet::::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from `std::collections::HashSet<[u8; 4]>` to `std::collections::HashSet` with mismatched layout is unsound - --> tests/ui/transmute_collection.rs:41:17 + --> tests/ui/transmute_collection.rs:42:17 | LL | let _ = transmute::<_, HashSet>(HashSet::<[u8; 4]>::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from `std::collections::BTreeMap` to `std::collections::BTreeMap` with mismatched layout is unsound - --> tests/ui/transmute_collection.rs:45:17 + --> tests/ui/transmute_collection.rs:46:17 | LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from `std::collections::BTreeMap` to `std::collections::BTreeMap` with mismatched layout is unsound - --> tests/ui/transmute_collection.rs:47:17 + --> tests/ui/transmute_collection.rs:48:17 | LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from `std::collections::BTreeMap` to `std::collections::BTreeMap` with mismatched layout is unsound - --> tests/ui/transmute_collection.rs:50:17 + --> tests/ui/transmute_collection.rs:51:17 | LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from `std::collections::BTreeMap<[u8; 4], u32>` to `std::collections::BTreeMap` with mismatched layout is unsound - --> tests/ui/transmute_collection.rs:52:17 + --> tests/ui/transmute_collection.rs:53:17 | LL | let _ = transmute::<_, BTreeMap>(BTreeMap::<[u8; 4], u32>::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from `std::collections::HashMap` to `std::collections::HashMap` with mismatched layout is unsound - --> tests/ui/transmute_collection.rs:56:17 + --> tests/ui/transmute_collection.rs:57:17 | LL | let _ = transmute::<_, HashMap>(HashMap::::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from `std::collections::HashMap` to `std::collections::HashMap` with mismatched layout is unsound - --> tests/ui/transmute_collection.rs:58:17 + --> tests/ui/transmute_collection.rs:59:17 | LL | let _ = transmute::<_, HashMap>(HashMap::::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from `std::collections::HashMap` to `std::collections::HashMap` with mismatched layout is unsound - --> tests/ui/transmute_collection.rs:61:17 + --> tests/ui/transmute_collection.rs:62:17 | LL | let _ = transmute::<_, HashMap>(HashMap::::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from `std::collections::HashMap<[u8; 4], u32>` to `std::collections::HashMap` with mismatched layout is unsound - --> tests/ui/transmute_collection.rs:63:17 + --> tests/ui/transmute_collection.rs:64:17 | LL | let _ = transmute::<_, HashMap>(HashMap::<[u8; 4], u32>::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/transmute_float_to_int.fixed b/src/tools/clippy/tests/ui/transmute_float_to_int.fixed index cef0bcfa623ae..82d5f7fdca105 100644 --- a/src/tools/clippy/tests/ui/transmute_float_to_int.fixed +++ b/src/tools/clippy/tests/ui/transmute_float_to_int.fixed @@ -1,4 +1,5 @@ #![warn(clippy::transmute_float_to_int)] +#![allow(clippy::missing_transmute_annotations)] fn float_to_int() { let _: u32 = unsafe { 1f32.to_bits() }; diff --git a/src/tools/clippy/tests/ui/transmute_float_to_int.rs b/src/tools/clippy/tests/ui/transmute_float_to_int.rs index 3d95bec2a20a9..9f056330adf97 100644 --- a/src/tools/clippy/tests/ui/transmute_float_to_int.rs +++ b/src/tools/clippy/tests/ui/transmute_float_to_int.rs @@ -1,4 +1,5 @@ #![warn(clippy::transmute_float_to_int)] +#![allow(clippy::missing_transmute_annotations)] fn float_to_int() { let _: u32 = unsafe { std::mem::transmute(1f32) }; diff --git a/src/tools/clippy/tests/ui/transmute_float_to_int.stderr b/src/tools/clippy/tests/ui/transmute_float_to_int.stderr index e89258d9102ac..ac3aae5f8b78c 100644 --- a/src/tools/clippy/tests/ui/transmute_float_to_int.stderr +++ b/src/tools/clippy/tests/ui/transmute_float_to_int.stderr @@ -1,5 +1,5 @@ error: transmute from a `f32` to a `u32` - --> tests/ui/transmute_float_to_int.rs:4:27 + --> tests/ui/transmute_float_to_int.rs:5:27 | LL | let _: u32 = unsafe { std::mem::transmute(1f32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits()` @@ -8,31 +8,31 @@ LL | let _: u32 = unsafe { std::mem::transmute(1f32) }; = help: to override `-D warnings` add `#[allow(clippy::transmute_float_to_int)]` error: transmute from a `f32` to a `i32` - --> tests/ui/transmute_float_to_int.rs:7:27 + --> tests/ui/transmute_float_to_int.rs:8:27 | LL | let _: i32 = unsafe { std::mem::transmute(1f32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits() as i32` error: transmute from a `f64` to a `u64` - --> tests/ui/transmute_float_to_int.rs:9:27 + --> tests/ui/transmute_float_to_int.rs:10:27 | LL | let _: u64 = unsafe { std::mem::transmute(1f64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits()` error: transmute from a `f64` to a `i64` - --> tests/ui/transmute_float_to_int.rs:11:27 + --> tests/ui/transmute_float_to_int.rs:12:27 | LL | let _: i64 = unsafe { std::mem::transmute(1f64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits() as i64` error: transmute from a `f64` to a `u64` - --> tests/ui/transmute_float_to_int.rs:13:27 + --> tests/ui/transmute_float_to_int.rs:14:27 | LL | let _: u64 = unsafe { std::mem::transmute(1.0) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1.0f64.to_bits()` error: transmute from a `f64` to a `u64` - --> tests/ui/transmute_float_to_int.rs:15:27 + --> tests/ui/transmute_float_to_int.rs:16:27 | LL | let _: u64 = unsafe { std::mem::transmute(-1.0) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-1.0f64).to_bits()` diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char.fixed b/src/tools/clippy/tests/ui/transmute_int_to_char.fixed index 1708011817541..d3277d1b8c732 100644 --- a/src/tools/clippy/tests/ui/transmute_int_to_char.fixed +++ b/src/tools/clippy/tests/ui/transmute_int_to_char.fixed @@ -1,4 +1,5 @@ #![warn(clippy::transmute_int_to_char)] +#![allow(clippy::missing_transmute_annotations)] fn int_to_char() { let _: char = unsafe { std::char::from_u32(0_u32).unwrap() }; diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char.rs b/src/tools/clippy/tests/ui/transmute_int_to_char.rs index 5846a97e88abf..d21c4fd6fea34 100644 --- a/src/tools/clippy/tests/ui/transmute_int_to_char.rs +++ b/src/tools/clippy/tests/ui/transmute_int_to_char.rs @@ -1,4 +1,5 @@ #![warn(clippy::transmute_int_to_char)] +#![allow(clippy::missing_transmute_annotations)] fn int_to_char() { let _: char = unsafe { std::mem::transmute(0_u32) }; diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char.stderr b/src/tools/clippy/tests/ui/transmute_int_to_char.stderr index 8444afbd21ef2..e3a3620f28b75 100644 --- a/src/tools/clippy/tests/ui/transmute_int_to_char.stderr +++ b/src/tools/clippy/tests/ui/transmute_int_to_char.stderr @@ -1,5 +1,5 @@ error: transmute from a `u32` to a `char` - --> tests/ui/transmute_int_to_char.rs:4:28 + --> tests/ui/transmute_int_to_char.rs:5:28 | LL | let _: char = unsafe { std::mem::transmute(0_u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_u32).unwrap()` @@ -8,7 +8,7 @@ LL | let _: char = unsafe { std::mem::transmute(0_u32) }; = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_char)]` error: transmute from a `i32` to a `char` - --> tests/ui/transmute_int_to_char.rs:7:28 + --> tests/ui/transmute_int_to_char.rs:8:28 | LL | let _: char = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()` diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.fixed b/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.fixed index 9ae4e11fb56e7..32a57645b46f1 100644 --- a/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.fixed +++ b/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.fixed @@ -1,6 +1,7 @@ #![no_std] #![feature(lang_items)] #![warn(clippy::transmute_int_to_char)] +#![allow(clippy::missing_transmute_annotations)] use core::panic::PanicInfo; diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.rs b/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.rs index 9a2afd5bd2fd6..942794c32f810 100644 --- a/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.rs +++ b/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.rs @@ -1,6 +1,7 @@ #![no_std] #![feature(lang_items)] #![warn(clippy::transmute_int_to_char)] +#![allow(clippy::missing_transmute_annotations)] use core::panic::PanicInfo; diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.stderr b/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.stderr index d2c3842b684e4..d94580a84d7a4 100644 --- a/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.stderr +++ b/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.stderr @@ -1,5 +1,5 @@ error: transmute from a `u32` to a `char` - --> tests/ui/transmute_int_to_char_no_std.rs:16:28 + --> tests/ui/transmute_int_to_char_no_std.rs:17:28 | LL | let _: char = unsafe { core::mem::transmute(0_u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `core::char::from_u32(0_u32).unwrap()` @@ -8,7 +8,7 @@ LL | let _: char = unsafe { core::mem::transmute(0_u32) }; = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_char)]` error: transmute from a `i32` to a `char` - --> tests/ui/transmute_int_to_char_no_std.rs:19:28 + --> tests/ui/transmute_int_to_char_no_std.rs:20:28 | LL | let _: char = unsafe { core::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `core::char::from_u32(0_i32 as u32).unwrap()` diff --git a/src/tools/clippy/tests/ui/transmute_int_to_non_zero.fixed b/src/tools/clippy/tests/ui/transmute_int_to_non_zero.fixed index 866c0bbf1271e..fe8db3dcb0cf9 100644 --- a/src/tools/clippy/tests/ui/transmute_int_to_non_zero.fixed +++ b/src/tools/clippy/tests/ui/transmute_int_to_non_zero.fixed @@ -1,4 +1,5 @@ #![warn(clippy::transmute_int_to_non_zero)] +#![allow(clippy::missing_transmute_annotations)] use core::num::*; diff --git a/src/tools/clippy/tests/ui/transmute_int_to_non_zero.rs b/src/tools/clippy/tests/ui/transmute_int_to_non_zero.rs index 803c4945c755a..a79ed5279b1f2 100644 --- a/src/tools/clippy/tests/ui/transmute_int_to_non_zero.rs +++ b/src/tools/clippy/tests/ui/transmute_int_to_non_zero.rs @@ -1,4 +1,5 @@ #![warn(clippy::transmute_int_to_non_zero)] +#![allow(clippy::missing_transmute_annotations)] use core::num::*; diff --git a/src/tools/clippy/tests/ui/transmute_int_to_non_zero.stderr b/src/tools/clippy/tests/ui/transmute_int_to_non_zero.stderr index dd37bd2105585..bb0b0d0ff4f06 100644 --- a/src/tools/clippy/tests/ui/transmute_int_to_non_zero.stderr +++ b/src/tools/clippy/tests/ui/transmute_int_to_non_zero.stderr @@ -1,5 +1,5 @@ error: transmute from a `u8` to a `NonZeroU8` - --> tests/ui/transmute_int_to_non_zero.rs:18:33 + --> tests/ui/transmute_int_to_non_zero.rs:19:33 | LL | let _: NonZeroU8 = unsafe { std::mem::transmute(int_u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroU8::new_unchecked(int_u8)` @@ -8,55 +8,55 @@ LL | let _: NonZeroU8 = unsafe { std::mem::transmute(int_u8) }; = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_non_zero)]` error: transmute from a `u16` to a `NonZeroU16` - --> tests/ui/transmute_int_to_non_zero.rs:21:34 + --> tests/ui/transmute_int_to_non_zero.rs:22:34 | LL | let _: NonZeroU16 = unsafe { std::mem::transmute(int_u16) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroU16::new_unchecked(int_u16)` error: transmute from a `u32` to a `NonZeroU32` - --> tests/ui/transmute_int_to_non_zero.rs:23:34 + --> tests/ui/transmute_int_to_non_zero.rs:24:34 | LL | let _: NonZeroU32 = unsafe { std::mem::transmute(int_u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroU32::new_unchecked(int_u32)` error: transmute from a `u64` to a `NonZeroU64` - --> tests/ui/transmute_int_to_non_zero.rs:25:34 + --> tests/ui/transmute_int_to_non_zero.rs:26:34 | LL | let _: NonZeroU64 = unsafe { std::mem::transmute(int_u64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroU64::new_unchecked(int_u64)` error: transmute from a `u128` to a `NonZeroU128` - --> tests/ui/transmute_int_to_non_zero.rs:27:35 + --> tests/ui/transmute_int_to_non_zero.rs:28:35 | LL | let _: NonZeroU128 = unsafe { std::mem::transmute(int_u128) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroU128::new_unchecked(int_u128)` error: transmute from a `i8` to a `NonZeroI8` - --> tests/ui/transmute_int_to_non_zero.rs:30:33 + --> tests/ui/transmute_int_to_non_zero.rs:31:33 | LL | let _: NonZeroI8 = unsafe { std::mem::transmute(int_i8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroI8::new_unchecked(int_i8)` error: transmute from a `i16` to a `NonZeroI16` - --> tests/ui/transmute_int_to_non_zero.rs:32:34 + --> tests/ui/transmute_int_to_non_zero.rs:33:34 | LL | let _: NonZeroI16 = unsafe { std::mem::transmute(int_i16) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroI16::new_unchecked(int_i16)` error: transmute from a `i32` to a `NonZeroI32` - --> tests/ui/transmute_int_to_non_zero.rs:34:34 + --> tests/ui/transmute_int_to_non_zero.rs:35:34 | LL | let _: NonZeroI32 = unsafe { std::mem::transmute(int_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroI32::new_unchecked(int_i32)` error: transmute from a `i64` to a `NonZeroI64` - --> tests/ui/transmute_int_to_non_zero.rs:36:34 + --> tests/ui/transmute_int_to_non_zero.rs:37:34 | LL | let _: NonZeroI64 = unsafe { std::mem::transmute(int_i64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroI64::new_unchecked(int_i64)` error: transmute from a `i128` to a `NonZeroI128` - --> tests/ui/transmute_int_to_non_zero.rs:38:35 + --> tests/ui/transmute_int_to_non_zero.rs:39:35 | LL | let _: NonZeroI128 = unsafe { std::mem::transmute(int_i128) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroI128::new_unchecked(int_i128)` diff --git a/src/tools/clippy/tests/ui/transmute_null_to_fn.rs b/src/tools/clippy/tests/ui/transmute_null_to_fn.rs index b07851e864f68..c0196ad52d437 100644 --- a/src/tools/clippy/tests/ui/transmute_null_to_fn.rs +++ b/src/tools/clippy/tests/ui/transmute_null_to_fn.rs @@ -1,6 +1,6 @@ #![allow(dead_code)] #![warn(clippy::transmute_null_to_fn)] -#![allow(clippy::zero_ptr)] +#![allow(clippy::zero_ptr, clippy::missing_transmute_annotations)] // Easy to lint because these only span one line. fn one_liners() { diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.fixed b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.fixed index 696def08f142d..b696a574ae392 100644 --- a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.fixed +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.fixed @@ -1,5 +1,5 @@ #![warn(clippy::transmute_ptr_to_ptr)] -#![allow(clippy::borrow_as_ptr)] +#![allow(clippy::borrow_as_ptr, clippy::missing_transmute_annotations)] // Make sure we can modify lifetimes, which is one of the recommended uses // of transmute diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs index 0700d8c19576a..85cc1d7802c2c 100644 --- a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs @@ -1,5 +1,5 @@ #![warn(clippy::transmute_ptr_to_ptr)] -#![allow(clippy::borrow_as_ptr)] +#![allow(clippy::borrow_as_ptr, clippy::missing_transmute_annotations)] // Make sure we can modify lifetimes, which is one of the recommended uses // of transmute diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed index acec14ccb6b82..56330d7193898 100644 --- a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed @@ -1,5 +1,9 @@ #![warn(clippy::transmute_ptr_to_ref)] -#![allow(clippy::match_single_binding, clippy::unnecessary_cast)] +#![allow( + clippy::match_single_binding, + clippy::unnecessary_cast, + clippy::missing_transmute_annotations +)] unsafe fn _ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { let _: &T = &*p; diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs index 3376401e284b5..ce1ee8bfbfaee 100644 --- a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs @@ -1,5 +1,9 @@ #![warn(clippy::transmute_ptr_to_ref)] -#![allow(clippy::match_single_binding, clippy::unnecessary_cast)] +#![allow( + clippy::match_single_binding, + clippy::unnecessary_cast, + clippy::missing_transmute_annotations +)] unsafe fn _ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { let _: &T = std::mem::transmute(p); diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr index d7d180398e1c6..44cda254c3f7b 100644 --- a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr @@ -1,5 +1,5 @@ error: transmute from a pointer type (`*const T`) to a reference type (`&T`) - --> tests/ui/transmute_ptr_to_ref.rs:5:17 + --> tests/ui/transmute_ptr_to_ref.rs:9:17 | LL | let _: &T = std::mem::transmute(p); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*p` @@ -8,127 +8,127 @@ LL | let _: &T = std::mem::transmute(p); = help: to override `-D warnings` add `#[allow(clippy::transmute_ptr_to_ref)]` error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`) - --> tests/ui/transmute_ptr_to_ref.rs:8:21 + --> tests/ui/transmute_ptr_to_ref.rs:12:21 | LL | let _: &mut T = std::mem::transmute(m); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *m` error: transmute from a pointer type (`*mut T`) to a reference type (`&T`) - --> tests/ui/transmute_ptr_to_ref.rs:11:17 + --> tests/ui/transmute_ptr_to_ref.rs:15:17 | LL | let _: &T = std::mem::transmute(m); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*m` error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`) - --> tests/ui/transmute_ptr_to_ref.rs:14:21 + --> tests/ui/transmute_ptr_to_ref.rs:18:21 | LL | let _: &mut T = std::mem::transmute(p as *mut T); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(p as *mut T)` error: transmute from a pointer type (`*const U`) to a reference type (`&T`) - --> tests/ui/transmute_ptr_to_ref.rs:17:17 + --> tests/ui/transmute_ptr_to_ref.rs:21:17 | LL | let _: &T = std::mem::transmute(o); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(o as *const T)` error: transmute from a pointer type (`*mut U`) to a reference type (`&mut T`) - --> tests/ui/transmute_ptr_to_ref.rs:20:21 + --> tests/ui/transmute_ptr_to_ref.rs:24:21 | LL | let _: &mut T = std::mem::transmute(om); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(om as *mut T)` error: transmute from a pointer type (`*mut U`) to a reference type (`&T`) - --> tests/ui/transmute_ptr_to_ref.rs:23:17 + --> tests/ui/transmute_ptr_to_ref.rs:27:17 | LL | let _: &T = std::mem::transmute(om); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(om as *const T)` error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, u8>`) - --> tests/ui/transmute_ptr_to_ref.rs:33:32 + --> tests/ui/transmute_ptr_to_ref.rs:37:32 | LL | let _: &Foo = unsafe { std::mem::transmute::<_, &Foo<_>>(raw) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::>()` error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, &u8>`) - --> tests/ui/transmute_ptr_to_ref.rs:35:33 + --> tests/ui/transmute_ptr_to_ref.rs:39:33 | LL | let _: &Foo<&u8> = unsafe { std::mem::transmute::<_, &Foo<&_>>(raw) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::>()` error: transmute from a pointer type (`*const i32`) to a reference type (`&u8`) - --> tests/ui/transmute_ptr_to_ref.rs:39:14 + --> tests/ui/transmute_ptr_to_ref.rs:43:14 | LL | unsafe { std::mem::transmute::<_, Bar>(raw) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const u8)` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:44:14 + --> tests/ui/transmute_ptr_to_ref.rs:48:14 | LL | 0 => std::mem::transmute(x), | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:45:14 + --> tests/ui/transmute_ptr_to_ref.rs:49:14 | LL | 1 => std::mem::transmute(y), | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:46:14 + --> tests/ui/transmute_ptr_to_ref.rs:50:14 | LL | 2 => std::mem::transmute::<_, &&'b u32>(x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:47:14 + --> tests/ui/transmute_ptr_to_ref.rs:51:14 | LL | _ => std::mem::transmute::<_, &&'b u32>(y), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&'b u32>()` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:55:19 + --> tests/ui/transmute_ptr_to_ref.rs:59:19 | LL | let _: &u32 = std::mem::transmute(a); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:56:19 + --> tests/ui/transmute_ptr_to_ref.rs:60:19 | LL | let _: &u32 = std::mem::transmute::<_, &u32>(a); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a.cast::()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:58:14 + --> tests/ui/transmute_ptr_to_ref.rs:62:14 | LL | 0 => std::mem::transmute(x), | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:59:14 + --> tests/ui/transmute_ptr_to_ref.rs:63:14 | LL | _ => std::mem::transmute::<_, &&'b u32>(x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:67:19 + --> tests/ui/transmute_ptr_to_ref.rs:71:19 | LL | let _: &u32 = std::mem::transmute(a); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:68:19 + --> tests/ui/transmute_ptr_to_ref.rs:72:19 | LL | let _: &u32 = std::mem::transmute::<_, &u32>(a); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a as *const u32)` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:70:14 + --> tests/ui/transmute_ptr_to_ref.rs:74:14 | LL | 0 => std::mem::transmute(x), | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &u32)` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:71:14 + --> tests/ui/transmute_ptr_to_ref.rs:75:14 | LL | _ => std::mem::transmute::<_, &&'b u32>(x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &'b u32)` diff --git a/src/tools/clippy/tests/ui/transmute_ref_to_ref.rs b/src/tools/clippy/tests/ui/transmute_ref_to_ref.rs index bdc7b9f647877..44d7af44a8053 100644 --- a/src/tools/clippy/tests/ui/transmute_ref_to_ref.rs +++ b/src/tools/clippy/tests/ui/transmute_ref_to_ref.rs @@ -1,7 +1,7 @@ //@no-rustfix #![deny(clippy::transmute_ptr_to_ptr)] -#![allow(dead_code)] +#![allow(dead_code, clippy::missing_transmute_annotations)] fn main() { unsafe { diff --git a/src/tools/clippy/tests/ui/transmute_ref_to_ref_no_std.rs b/src/tools/clippy/tests/ui/transmute_ref_to_ref_no_std.rs index b67386f858852..5917705875496 100644 --- a/src/tools/clippy/tests/ui/transmute_ref_to_ref_no_std.rs +++ b/src/tools/clippy/tests/ui/transmute_ref_to_ref_no_std.rs @@ -1,7 +1,7 @@ //@no-rustfix #![deny(clippy::transmute_ptr_to_ptr)] -#![allow(dead_code)] +#![allow(dead_code, clippy::missing_transmute_annotations)] #![feature(lang_items)] #![no_std] diff --git a/src/tools/clippy/tests/ui/transmute_undefined_repr.rs b/src/tools/clippy/tests/ui/transmute_undefined_repr.rs index a087d09c1202a..dd4bac7f1ed72 100644 --- a/src/tools/clippy/tests/ui/transmute_undefined_repr.rs +++ b/src/tools/clippy/tests/ui/transmute_undefined_repr.rs @@ -1,5 +1,10 @@ #![warn(clippy::transmute_undefined_repr)] -#![allow(clippy::unit_arg, clippy::transmute_ptr_to_ref, clippy::useless_transmute)] +#![allow( + clippy::unit_arg, + clippy::transmute_ptr_to_ref, + clippy::useless_transmute, + clippy::missing_transmute_annotations +)] use core::any::TypeId; use core::ffi::c_void; diff --git a/src/tools/clippy/tests/ui/transmute_undefined_repr.stderr b/src/tools/clippy/tests/ui/transmute_undefined_repr.stderr index 5504fbe16e416..b41d37a5cd132 100644 --- a/src/tools/clippy/tests/ui/transmute_undefined_repr.stderr +++ b/src/tools/clippy/tests/ui/transmute_undefined_repr.stderr @@ -1,5 +1,5 @@ error: transmute from `Ty2` which has an undefined layout - --> tests/ui/transmute_undefined_repr.rs:29:33 + --> tests/ui/transmute_undefined_repr.rs:34:33 | LL | let _: Ty2C = transmute(value::>()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,13 +8,13 @@ LL | let _: Ty2C = transmute(value::>()); = help: to override `-D warnings` add `#[allow(clippy::transmute_undefined_repr)]` error: transmute into `Ty2` which has an undefined layout - --> tests/ui/transmute_undefined_repr.rs:33:32 + --> tests/ui/transmute_undefined_repr.rs:38:32 | LL | let _: Ty2 = transmute(value::>()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from `Ty>` to `Ty2`, both of which have an undefined layout - --> tests/ui/transmute_undefined_repr.rs:42:32 + --> tests/ui/transmute_undefined_repr.rs:47:32 | LL | let _: Ty2 = transmute(value::>>()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | let _: Ty2 = transmute(value::>>()); = note: two instances of the same generic type (`Ty2`) may have different layouts error: transmute from `Ty2` to `Ty>`, both of which have an undefined layout - --> tests/ui/transmute_undefined_repr.rs:46:36 + --> tests/ui/transmute_undefined_repr.rs:51:36 | LL | let _: Ty> = transmute(value::>()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -30,7 +30,7 @@ LL | let _: Ty> = transmute(value::>()); = note: two instances of the same generic type (`Ty2`) may have different layouts error: transmute from `Ty<&Ty2>` to `&Ty2`, both of which have an undefined layout - --> tests/ui/transmute_undefined_repr.rs:54:33 + --> tests/ui/transmute_undefined_repr.rs:59:33 | LL | let _: &Ty2 = transmute(value::>>()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -38,7 +38,7 @@ LL | let _: &Ty2 = transmute(value::>>()); = note: two instances of the same generic type (`Ty2`) may have different layouts error: transmute from `&Ty2` to `Ty<&Ty2>`, both of which have an undefined layout - --> tests/ui/transmute_undefined_repr.rs:58:37 + --> tests/ui/transmute_undefined_repr.rs:63:37 | LL | let _: Ty<&Ty2> = transmute(value::<&Ty2>()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -46,7 +46,7 @@ LL | let _: Ty<&Ty2> = transmute(value::<&Ty2>()); = note: two instances of the same generic type (`Ty2`) may have different layouts error: transmute from `std::boxed::Box>` to `&mut Ty2`, both of which have an undefined layout - --> tests/ui/transmute_undefined_repr.rs:88:45 + --> tests/ui/transmute_undefined_repr.rs:93:45 | LL | let _: &'static mut Ty2 = transmute(value::>>()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -54,7 +54,7 @@ LL | let _: &'static mut Ty2 = transmute(value::` to `std::boxed::Box>`, both of which have an undefined layout - --> tests/ui/transmute_undefined_repr.rs:92:37 + --> tests/ui/transmute_undefined_repr.rs:97:37 | LL | let _: Box> = transmute(value::<&'static mut Ty2>()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,7 +62,7 @@ LL | let _: Box> = transmute(value::<&'static mut Ty2` which has an undefined layout - --> tests/ui/transmute_undefined_repr.rs:189:39 + --> tests/ui/transmute_undefined_repr.rs:194:39 | LL | let _: *const Ty2 = transmute(value::<*const Ty2C>>()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -70,7 +70,7 @@ LL | let _: *const Ty2 = transmute(value::<*const Ty2C` has an undefined layout error: transmute from `*const Ty2` which has an undefined layout - --> tests/ui/transmute_undefined_repr.rs:193:50 + --> tests/ui/transmute_undefined_repr.rs:198:50 | LL | let _: *const Ty2C> = transmute(value::<*const Ty2>()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -78,7 +78,7 @@ LL | let _: *const Ty2C> = transmute(value::<*const T = note: the contained type `Ty2` has an undefined layout error: transmute from `std::vec::Vec>` to `std::vec::Vec>`, both of which have an undefined layout - --> tests/ui/transmute_undefined_repr.rs:240:35 + --> tests/ui/transmute_undefined_repr.rs:245:35 | LL | let _: Vec> = transmute(value::>>()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -86,7 +86,7 @@ LL | let _: Vec> = transmute(value::>>()); = note: two instances of the same generic type (`Vec`) may have different layouts error: transmute from `std::vec::Vec>` to `std::vec::Vec>`, both of which have an undefined layout - --> tests/ui/transmute_undefined_repr.rs:244:35 + --> tests/ui/transmute_undefined_repr.rs:249:35 | LL | let _: Vec> = transmute(value::>>()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed index 2365695d6914b..51682da4a9885 100644 --- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed +++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed @@ -3,7 +3,7 @@ // would otherwise be responsible for #![warn(clippy::useless_transmute)] #![warn(clippy::transmute_ptr_to_ptr)] -#![allow(unused, clippy::borrow_as_ptr)] +#![allow(unused, clippy::borrow_as_ptr, clippy::missing_transmute_annotations)] use std::mem::{size_of, transmute}; diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs index cd1607b4c1999..e5fcdef7a1c39 100644 --- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs +++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs @@ -3,7 +3,7 @@ // would otherwise be responsible for #![warn(clippy::useless_transmute)] #![warn(clippy::transmute_ptr_to_ptr)] -#![allow(unused, clippy::borrow_as_ptr)] +#![allow(unused, clippy::borrow_as_ptr, clippy::missing_transmute_annotations)] use std::mem::{size_of, transmute}; diff --git a/src/tools/clippy/tests/ui/transmuting_null.rs b/src/tools/clippy/tests/ui/transmuting_null.rs index 88b8c9965237d..c2deb6b6c460c 100644 --- a/src/tools/clippy/tests/ui/transmuting_null.rs +++ b/src/tools/clippy/tests/ui/transmuting_null.rs @@ -2,7 +2,7 @@ #![warn(clippy::transmuting_null)] #![allow(clippy::zero_ptr)] #![allow(clippy::transmute_ptr_to_ref)] -#![allow(clippy::eq_op)] +#![allow(clippy::eq_op, clippy::missing_transmute_annotations)] // Easy to lint because these only span one line. fn one_liners() { diff --git a/src/tools/clippy/tests/ui/type_id_on_box.fixed b/src/tools/clippy/tests/ui/type_id_on_box.fixed index 538c38b70e6b3..3656043700fa9 100644 --- a/src/tools/clippy/tests/ui/type_id_on_box.fixed +++ b/src/tools/clippy/tests/ui/type_id_on_box.fixed @@ -19,19 +19,37 @@ fn existential() -> impl Any { Box::new(1) as Box } +trait AnySubTrait: Any {} +impl AnySubTrait for T {} + fn main() { + // Don't lint, calling `.type_id()` on a `&dyn Any` does the expected thing + let ref_dyn: &dyn Any = &42; + let _ = ref_dyn.type_id(); + let any_box: Box = Box::new(0usize); let _ = (*any_box).type_id(); - let _ = TypeId::of::>(); // Don't lint. We explicitly say "do this instead" if this is intentional + //~^ ERROR: calling `.type_id()` on + + // Don't lint. We explicitly say "do this instead" if this is intentional + let _ = TypeId::of::>(); let _ = (*any_box).type_id(); + + // 2 derefs are needed here to get to the `dyn Any` let any_box: &Box = &(Box::new(0usize) as Box); - let _ = (**any_box).type_id(); // 2 derefs are needed here to get to the `dyn Any` + let _ = (**any_box).type_id(); + //~^ ERROR: calling `.type_id()` on let b = existential(); - let _ = b.type_id(); // Don't lint. + let _ = b.type_id(); // Don't + + let b: Box = Box::new(1); + let _ = (*b).type_id(); + //~^ ERROR: calling `.type_id()` on let b: SomeBox = Box::new(0usize); let _ = (*b).type_id(); + //~^ ERROR: calling `.type_id()` on let b = BadBox(Box::new(0usize)); let _ = b.type_id(); // Don't lint. This is a call to `::type_id`. Not `std::boxed::Box`! diff --git a/src/tools/clippy/tests/ui/type_id_on_box.rs b/src/tools/clippy/tests/ui/type_id_on_box.rs index f224d273bc236..4bd9e73f2da02 100644 --- a/src/tools/clippy/tests/ui/type_id_on_box.rs +++ b/src/tools/clippy/tests/ui/type_id_on_box.rs @@ -19,19 +19,37 @@ fn existential() -> impl Any { Box::new(1) as Box } +trait AnySubTrait: Any {} +impl AnySubTrait for T {} + fn main() { + // Don't lint, calling `.type_id()` on a `&dyn Any` does the expected thing + let ref_dyn: &dyn Any = &42; + let _ = ref_dyn.type_id(); + let any_box: Box = Box::new(0usize); let _ = any_box.type_id(); - let _ = TypeId::of::>(); // Don't lint. We explicitly say "do this instead" if this is intentional + //~^ ERROR: calling `.type_id()` on + + // Don't lint. We explicitly say "do this instead" if this is intentional + let _ = TypeId::of::>(); let _ = (*any_box).type_id(); + + // 2 derefs are needed here to get to the `dyn Any` let any_box: &Box = &(Box::new(0usize) as Box); - let _ = any_box.type_id(); // 2 derefs are needed here to get to the `dyn Any` + let _ = any_box.type_id(); + //~^ ERROR: calling `.type_id()` on let b = existential(); - let _ = b.type_id(); // Don't lint. + let _ = b.type_id(); // Don't + + let b: Box = Box::new(1); + let _ = b.type_id(); + //~^ ERROR: calling `.type_id()` on let b: SomeBox = Box::new(0usize); let _ = b.type_id(); + //~^ ERROR: calling `.type_id()` on let b = BadBox(Box::new(0usize)); let _ = b.type_id(); // Don't lint. This is a call to `::type_id`. Not `std::boxed::Box`! diff --git a/src/tools/clippy/tests/ui/type_id_on_box.stderr b/src/tools/clippy/tests/ui/type_id_on_box.stderr index 0fce6a37c0044..4528195607dad 100644 --- a/src/tools/clippy/tests/ui/type_id_on_box.stderr +++ b/src/tools/clippy/tests/ui/type_id_on_box.stderr @@ -1,37 +1,48 @@ -error: calling `.type_id()` on a `Box` - --> tests/ui/type_id_on_box.rs:24:13 +error: calling `.type_id()` on `Box` + --> tests/ui/type_id_on_box.rs:31:13 | LL | let _ = any_box.type_id(); | -------^^^^^^^^^^ | | | help: consider dereferencing first: `(*any_box)` | - = note: this returns the type id of the literal type `Box` instead of the type id of the boxed value, which is most likely not what you want + = note: this returns the type id of the literal type `Box<_>` instead of the type id of the boxed value, which is most likely not what you want = note: if this is intentional, use `TypeId::of::>()` instead, which makes it more clear = note: `-D clippy::type-id-on-box` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::type_id_on_box)]` -error: calling `.type_id()` on a `Box` - --> tests/ui/type_id_on_box.rs:28:13 +error: calling `.type_id()` on `Box` + --> tests/ui/type_id_on_box.rs:40:13 | -LL | let _ = any_box.type_id(); // 2 derefs are needed here to get to the `dyn Any` +LL | let _ = any_box.type_id(); | -------^^^^^^^^^^ | | | help: consider dereferencing first: `(**any_box)` | - = note: this returns the type id of the literal type `Box` instead of the type id of the boxed value, which is most likely not what you want + = note: this returns the type id of the literal type `Box<_>` instead of the type id of the boxed value, which is most likely not what you want = note: if this is intentional, use `TypeId::of::>()` instead, which makes it more clear -error: calling `.type_id()` on a `Box` - --> tests/ui/type_id_on_box.rs:34:13 +error: calling `.type_id()` on `Box` + --> tests/ui/type_id_on_box.rs:47:13 + | +LL | let _ = b.type_id(); + | -^^^^^^^^^^ + | | + | help: consider dereferencing first: `(*b)` + | + = note: this returns the type id of the literal type `Box<_>` instead of the type id of the boxed value, which is most likely not what you want + = note: if this is intentional, use `TypeId::of::>()` instead, which makes it more clear + +error: calling `.type_id()` on `Box` + --> tests/ui/type_id_on_box.rs:51:13 | LL | let _ = b.type_id(); | -^^^^^^^^^^ | | | help: consider dereferencing first: `(*b)` | - = note: this returns the type id of the literal type `Box` instead of the type id of the boxed value, which is most likely not what you want + = note: this returns the type id of the literal type `Box<_>` instead of the type id of the boxed value, which is most likely not what you want = note: if this is intentional, use `TypeId::of::>()` instead, which makes it more clear -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/type_id_on_box_unfixable.rs b/src/tools/clippy/tests/ui/type_id_on_box_unfixable.rs new file mode 100644 index 0000000000000..f6d09834adb17 --- /dev/null +++ b/src/tools/clippy/tests/ui/type_id_on_box_unfixable.rs @@ -0,0 +1,31 @@ +#![warn(clippy::type_id_on_box)] + +use std::any::{Any, TypeId}; +use std::ops::Deref; + +trait AnySubTrait: Any {} +impl AnySubTrait for T {} + +// `Any` is an indirect supertrait +trait AnySubSubTrait: AnySubTrait {} +impl AnySubSubTrait for T {} + +// This trait mentions `Any` in its predicates, but it is not a subtrait of `Any`. +trait NormalTrait +where + i32: Any, +{ +} +impl NormalTrait for T {} + +fn main() { + // (currently we don't look deeper than one level into the supertrait hierachy, but we probably + // could) + let b: Box = Box::new(1); + let _ = b.type_id(); + //~^ ERROR: calling `.type_id()` on + + let b: Box = Box::new(1); + let _ = b.type_id(); + //~^ ERROR: calling `.type_id()` on +} diff --git a/src/tools/clippy/tests/ui/type_id_on_box_unfixable.stderr b/src/tools/clippy/tests/ui/type_id_on_box_unfixable.stderr new file mode 100644 index 0000000000000..539ed481ec101 --- /dev/null +++ b/src/tools/clippy/tests/ui/type_id_on_box_unfixable.stderr @@ -0,0 +1,22 @@ +error: calling `.type_id()` on `Box` + --> tests/ui/type_id_on_box_unfixable.rs:25:13 + | +LL | let _ = b.type_id(); + | ^^^^^^^^^^^ + | + = note: this returns the type id of the literal type `Box<_>` instead of the type id of the boxed value, which is most likely not what you want + = note: if this is intentional, use `TypeId::of::>()` instead, which makes it more clear + = note: `-D clippy::type-id-on-box` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::type_id_on_box)]` + +error: calling `.type_id()` on `Box` + --> tests/ui/type_id_on_box_unfixable.rs:29:13 + | +LL | let _ = b.type_id(); + | ^^^^^^^^^^^ + | + = note: this returns the type id of the literal type `Box<_>` instead of the type id of the boxed value, which is most likely not what you want + = note: if this is intentional, use `TypeId::of::>()` instead, which makes it more clear + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/uninhabited_references.rs b/src/tools/clippy/tests/ui/uninhabited_references.rs index cd07b590a616a..3569366ed0560 100644 --- a/src/tools/clippy/tests/ui/uninhabited_references.rs +++ b/src/tools/clippy/tests/ui/uninhabited_references.rs @@ -1,4 +1,5 @@ #![warn(clippy::uninhabited_references)] +#![allow(clippy::missing_transmute_annotations)] #![feature(never_type)] fn ret_uninh_ref() -> &'static std::convert::Infallible { diff --git a/src/tools/clippy/tests/ui/uninhabited_references.stderr b/src/tools/clippy/tests/ui/uninhabited_references.stderr index 446d4e7555703..8c9b206f42967 100644 --- a/src/tools/clippy/tests/ui/uninhabited_references.stderr +++ b/src/tools/clippy/tests/ui/uninhabited_references.stderr @@ -1,5 +1,5 @@ error: dereferencing a reference to an uninhabited type would be undefined behavior - --> tests/ui/uninhabited_references.rs:4:23 + --> tests/ui/uninhabited_references.rs:5:23 | LL | fn ret_uninh_ref() -> &'static std::convert::Infallible { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | fn ret_uninh_ref() -> &'static std::convert::Infallible { = help: to override `-D warnings` add `#[allow(clippy::uninhabited_references)]` error: dereferencing a reference to an uninhabited type would be undefined behavior - --> tests/ui/uninhabited_references.rs:10:30 + --> tests/ui/uninhabited_references.rs:11:30 | LL | fn $name(x: &$ty) -> &$ty { | ^^^^ @@ -19,7 +19,7 @@ LL | ret_something!(id_never, !); = note: this error originates in the macro `ret_something` (in Nightly builds, run with -Z macro-backtrace for more info) error: dereferencing a reference to an uninhabited type is undefined behavior - --> tests/ui/uninhabited_references.rs:11:14 + --> tests/ui/uninhabited_references.rs:12:14 | LL | &*x | ^^ @@ -30,7 +30,7 @@ LL | ret_something!(id_never, !); = note: this error originates in the macro `ret_something` (in Nightly builds, run with -Z macro-backtrace for more info) error: dereferencing a reference to an uninhabited type is undefined behavior - --> tests/ui/uninhabited_references.rs:21:13 + --> tests/ui/uninhabited_references.rs:22:13 | LL | let _ = *x; | ^^ diff --git a/src/tools/clippy/tests/ui/use_self.fixed b/src/tools/clippy/tests/ui/use_self.fixed index 6ea7857a238da..ffc5b74d7bd68 100644 --- a/src/tools/clippy/tests/ui/use_self.fixed +++ b/src/tools/clippy/tests/ui/use_self.fixed @@ -7,7 +7,8 @@ clippy::upper_case_acronyms, clippy::from_over_into, clippy::self_named_constructors, - clippy::needless_lifetimes + clippy::needless_lifetimes, + clippy::missing_transmute_annotations )] #[macro_use] diff --git a/src/tools/clippy/tests/ui/use_self.rs b/src/tools/clippy/tests/ui/use_self.rs index 338cc00e45a81..eb9d96168bcd1 100644 --- a/src/tools/clippy/tests/ui/use_self.rs +++ b/src/tools/clippy/tests/ui/use_self.rs @@ -7,7 +7,8 @@ clippy::upper_case_acronyms, clippy::from_over_into, clippy::self_named_constructors, - clippy::needless_lifetimes + clippy::needless_lifetimes, + clippy::missing_transmute_annotations )] #[macro_use] diff --git a/src/tools/clippy/tests/ui/use_self.stderr b/src/tools/clippy/tests/ui/use_self.stderr index d7aa8410a47be..bd5b685b45d54 100644 --- a/src/tools/clippy/tests/ui/use_self.stderr +++ b/src/tools/clippy/tests/ui/use_self.stderr @@ -1,5 +1,5 @@ error: unnecessary structure name repetition - --> tests/ui/use_self.rs:22:21 + --> tests/ui/use_self.rs:23:21 | LL | fn new() -> Foo { | ^^^ help: use the applicable keyword: `Self` @@ -8,253 +8,253 @@ LL | fn new() -> Foo { = help: to override `-D warnings` add `#[allow(clippy::use_self)]` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:23:13 + --> tests/ui/use_self.rs:24:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:25:22 + --> tests/ui/use_self.rs:26:22 | LL | fn test() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:26:13 + --> tests/ui/use_self.rs:27:13 | LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:31:25 + --> tests/ui/use_self.rs:32:25 | LL | fn default() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:32:13 + --> tests/ui/use_self.rs:33:13 | LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:73:28 + --> tests/ui/use_self.rs:74:28 | LL | fn clone(&self) -> Foo<'a> { | ^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:106:24 + --> tests/ui/use_self.rs:107:24 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:106:55 + --> tests/ui/use_self.rs:107:55 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:121:13 + --> tests/ui/use_self.rs:122:13 | LL | TS(0) | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:156:29 + --> tests/ui/use_self.rs:157:29 | LL | fn bar() -> Bar { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:157:21 + --> tests/ui/use_self.rs:158:21 | LL | Bar { foo: Foo {} } | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:168:21 + --> tests/ui/use_self.rs:169:21 | LL | fn baz() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:169:13 + --> tests/ui/use_self.rs:170:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:186:21 + --> tests/ui/use_self.rs:187:21 | LL | let _ = Enum::B(42); | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:187:21 + --> tests/ui/use_self.rs:188:21 | LL | let _ = Enum::C { field: true }; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:188:21 + --> tests/ui/use_self.rs:189:21 | LL | let _ = Enum::A; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:230:13 + --> tests/ui/use_self.rs:231:13 | LL | nested::A::fun_1(); | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:231:13 + --> tests/ui/use_self.rs:232:13 | LL | nested::A::A; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:233:13 + --> tests/ui/use_self.rs:234:13 | LL | nested::A {}; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:252:13 + --> tests/ui/use_self.rs:253:13 | LL | TestStruct::from_something() | ^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:266:25 + --> tests/ui/use_self.rs:267:25 | LL | async fn g() -> S { | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:267:13 + --> tests/ui/use_self.rs:268:13 | LL | S {} | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:271:16 + --> tests/ui/use_self.rs:272:16 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:271:22 + --> tests/ui/use_self.rs:272:22 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:294:29 + --> tests/ui/use_self.rs:295:29 | LL | fn foo(value: T) -> Foo { | ^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:295:13 + --> tests/ui/use_self.rs:296:13 | LL | Foo:: { value } | ^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:467:13 + --> tests/ui/use_self.rs:468:13 | LL | A::new::(submod::B {}) | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:504:13 + --> tests/ui/use_self.rs:505:13 | LL | S2::new() | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:541:17 + --> tests/ui/use_self.rs:542:17 | LL | Foo::Bar => unimplemented!(), | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:542:17 + --> tests/ui/use_self.rs:543:17 | LL | Foo::Baz => unimplemented!(), | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:548:20 + --> tests/ui/use_self.rs:549:20 | LL | if let Foo::Bar = self { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:572:17 + --> tests/ui/use_self.rs:573:17 | LL | Something::Num(n) => *n, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:573:17 + --> tests/ui/use_self.rs:574:17 | LL | Something::TupleNums(n, _m) => *n, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:574:17 + --> tests/ui/use_self.rs:575:17 | LL | Something::StructNums { one, two: _ } => *one, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:580:17 + --> tests/ui/use_self.rs:581:17 | LL | crate::issue8845::Something::Num(n) => *n, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:581:17 + --> tests/ui/use_self.rs:582:17 | LL | crate::issue8845::Something::TupleNums(n, _m) => *n, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:582:17 + --> tests/ui/use_self.rs:583:17 | LL | crate::issue8845::Something::StructNums { one, two: _ } => *one, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:598:17 + --> tests/ui/use_self.rs:599:17 | LL | let Foo(x) = self; | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:603:17 + --> tests/ui/use_self.rs:604:17 | LL | let crate::issue8845::Foo(x) = self; | ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:610:17 + --> tests/ui/use_self.rs:611:17 | LL | let Bar { x, .. } = self; | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:615:17 + --> tests/ui/use_self.rs:616:17 | LL | let crate::issue8845::Bar { x, .. } = self; | ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:654:17 + --> tests/ui/use_self.rs:655:17 | LL | E::A => {}, | ^ help: use the applicable keyword: `Self` diff --git a/src/tools/clippy/tests/ui/useless_asref.fixed b/src/tools/clippy/tests/ui/useless_asref.fixed index c98f2928e03cc..ddbb9255b4667 100644 --- a/src/tools/clippy/tests/ui/useless_asref.fixed +++ b/src/tools/clippy/tests/ui/useless_asref.fixed @@ -8,6 +8,8 @@ )] use std::fmt::Debug; +use std::rc::{Rc, Weak as RcWeak}; +use std::sync::{Arc, Weak as ArcWeak}; struct FakeAsRef; @@ -180,6 +182,22 @@ mod issue12135 { } } +fn issue_12528() { + struct Foo; + + let opt = Some(Arc::new(Foo)); + let _ = opt.as_ref().map(Arc::clone); + + let opt = Some(Rc::new(Foo)); + let _ = opt.as_ref().map(Rc::clone); + + let opt = Some(Arc::downgrade(&Arc::new(Foo))); + let _ = opt.as_ref().map(ArcWeak::clone); + + let opt = Some(Rc::downgrade(&Rc::new(Foo))); + let _ = opt.as_ref().map(RcWeak::clone); +} + fn main() { not_ok(); ok(); diff --git a/src/tools/clippy/tests/ui/useless_asref.rs b/src/tools/clippy/tests/ui/useless_asref.rs index f9d603f116de5..b0405e930a259 100644 --- a/src/tools/clippy/tests/ui/useless_asref.rs +++ b/src/tools/clippy/tests/ui/useless_asref.rs @@ -8,6 +8,8 @@ )] use std::fmt::Debug; +use std::rc::{Rc, Weak as RcWeak}; +use std::sync::{Arc, Weak as ArcWeak}; struct FakeAsRef; @@ -180,6 +182,22 @@ mod issue12135 { } } +fn issue_12528() { + struct Foo; + + let opt = Some(Arc::new(Foo)); + let _ = opt.as_ref().map(Arc::clone); + + let opt = Some(Rc::new(Foo)); + let _ = opt.as_ref().map(Rc::clone); + + let opt = Some(Arc::downgrade(&Arc::new(Foo))); + let _ = opt.as_ref().map(ArcWeak::clone); + + let opt = Some(Rc::downgrade(&Rc::new(Foo))); + let _ = opt.as_ref().map(RcWeak::clone); +} + fn main() { not_ok(); ok(); diff --git a/src/tools/clippy/tests/ui/useless_asref.stderr b/src/tools/clippy/tests/ui/useless_asref.stderr index c7d622ec2dcaf..5f495c396705c 100644 --- a/src/tools/clippy/tests/ui/useless_asref.stderr +++ b/src/tools/clippy/tests/ui/useless_asref.stderr @@ -1,5 +1,5 @@ error: this call to `as_ref` does nothing - --> tests/ui/useless_asref.rs:48:18 + --> tests/ui/useless_asref.rs:50:18 | LL | foo_rstr(rstr.as_ref()); | ^^^^^^^^^^^^^ help: try: `rstr` @@ -11,103 +11,103 @@ LL | #![deny(clippy::useless_asref)] | ^^^^^^^^^^^^^^^^^^^^^ error: this call to `as_ref` does nothing - --> tests/ui/useless_asref.rs:50:20 + --> tests/ui/useless_asref.rs:52:20 | LL | foo_rslice(rslice.as_ref()); | ^^^^^^^^^^^^^^^ help: try: `rslice` error: this call to `as_mut` does nothing - --> tests/ui/useless_asref.rs:54:21 + --> tests/ui/useless_asref.rs:56:21 | LL | foo_mrslice(mrslice.as_mut()); | ^^^^^^^^^^^^^^^^ help: try: `mrslice` error: this call to `as_ref` does nothing - --> tests/ui/useless_asref.rs:56:20 + --> tests/ui/useless_asref.rs:58:20 | LL | foo_rslice(mrslice.as_ref()); | ^^^^^^^^^^^^^^^^ help: try: `mrslice` error: this call to `as_ref` does nothing - --> tests/ui/useless_asref.rs:63:20 + --> tests/ui/useless_asref.rs:65:20 | LL | foo_rslice(rrrrrslice.as_ref()); | ^^^^^^^^^^^^^^^^^^^ help: try: `rrrrrslice` error: this call to `as_ref` does nothing - --> tests/ui/useless_asref.rs:65:18 + --> tests/ui/useless_asref.rs:67:18 | LL | foo_rstr(rrrrrstr.as_ref()); | ^^^^^^^^^^^^^^^^^ help: try: `rrrrrstr` error: this call to `as_mut` does nothing - --> tests/ui/useless_asref.rs:70:21 + --> tests/ui/useless_asref.rs:72:21 | LL | foo_mrslice(mrrrrrslice.as_mut()); | ^^^^^^^^^^^^^^^^^^^^ help: try: `mrrrrrslice` error: this call to `as_ref` does nothing - --> tests/ui/useless_asref.rs:72:20 + --> tests/ui/useless_asref.rs:74:20 | LL | foo_rslice(mrrrrrslice.as_ref()); | ^^^^^^^^^^^^^^^^^^^^ help: try: `mrrrrrslice` error: this call to `as_ref` does nothing - --> tests/ui/useless_asref.rs:76:16 + --> tests/ui/useless_asref.rs:78:16 | LL | foo_rrrrmr((&&&&MoreRef).as_ref()); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&&&&MoreRef)` error: this call to `as_mut` does nothing - --> tests/ui/useless_asref.rs:126:13 + --> tests/ui/useless_asref.rs:128:13 | LL | foo_mrt(mrt.as_mut()); | ^^^^^^^^^^^^ help: try: `mrt` error: this call to `as_ref` does nothing - --> tests/ui/useless_asref.rs:128:12 + --> tests/ui/useless_asref.rs:130:12 | LL | foo_rt(mrt.as_ref()); | ^^^^^^^^^^^^ help: try: `mrt` error: this call to `as_ref.map(...)` does nothing - --> tests/ui/useless_asref.rs:139:13 + --> tests/ui/useless_asref.rs:141:13 | LL | let z = x.as_ref().map(String::clone); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.clone()` error: this call to `as_ref.map(...)` does nothing - --> tests/ui/useless_asref.rs:141:13 + --> tests/ui/useless_asref.rs:143:13 | LL | let z = x.as_ref().map(|z| z.clone()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.clone()` error: this call to `as_ref.map(...)` does nothing - --> tests/ui/useless_asref.rs:143:13 + --> tests/ui/useless_asref.rs:145:13 | LL | let z = x.as_ref().map(|z| String::clone(z)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.clone()` error: this call to `as_ref.map(...)` does nothing - --> tests/ui/useless_asref.rs:167:9 + --> tests/ui/useless_asref.rs:169:9 | LL | x.field.as_ref().map(|v| v.clone()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.field.clone()` error: this call to `as_ref.map(...)` does nothing - --> tests/ui/useless_asref.rs:169:9 + --> tests/ui/useless_asref.rs:171:9 | LL | x.field.as_ref().map(Clone::clone); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.field.clone()` error: this call to `as_ref.map(...)` does nothing - --> tests/ui/useless_asref.rs:171:9 + --> tests/ui/useless_asref.rs:173:9 | LL | x.field.as_ref().map(|v| Clone::clone(v)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.field.clone()` error: this call to `as_ref.map(...)` does nothing - --> tests/ui/useless_asref.rs:176:9 + --> tests/ui/useless_asref.rs:178:9 | LL | Some(1).as_ref().map(|&x| x.clone()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(1).clone()` diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index d455d967e30c0..d8131044ff2ed 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -19,7 +19,7 @@ new_pr = true [assign] contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" -users_on_vacation = [] +users_on_vacation = ["y21"] [assign.owners] "/.github" = ["@flip1995"] diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index b791b38379f9c..1624e2a60843a 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -571,7 +571,9 @@ pub fn make_tests( &modified_tests, &mut poisoned, ) - .unwrap_or_else(|_| panic!("Could not read tests from {}", config.src_base.display())); + .unwrap_or_else(|reason| { + panic!("Could not read tests from {}: {reason}", config.src_base.display()) + }); if poisoned { eprintln!(); diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index bd2f65925bb96..689fdc5dfebc0 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -3926,11 +3926,17 @@ impl<'test> TestCx<'test> { cmd.env("IS_MSVC", "1") .env("IS_WINDOWS", "1") .env("MSVC_LIB", format!("'{}' -nologo", lib.display())) - .env("CC", format!("'{}' {}", self.config.cc, cflags)) - .env("CXX", format!("'{}' {}", &self.config.cxx, cxxflags)); + // Note: we diverge from legacy run_make and don't lump `CC` the compiler and + // default flags together. + .env("CC_DEFAULT_FLAGS", &cflags) + .env("CC", &self.config.cc) + .env("CXX_DEFAULT_FLAGS", &cxxflags) + .env("CXX", &self.config.cxx); } else { - cmd.env("CC", format!("{} {}", self.config.cc, self.config.cflags)) - .env("CXX", format!("{} {}", self.config.cxx, self.config.cxxflags)) + cmd.env("CC_DEFAULT_FLAGS", &self.config.cflags) + .env("CC", &self.config.cc) + .env("CXX_DEFAULT_FLAGS", &self.config.cxxflags) + .env("CXX", &self.config.cxx) .env("AR", &self.config.ar); if self.config.target.contains("windows") { diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml index 041254e6f43a4..9d24d3c6f4779 100644 --- a/src/tools/miri/Cargo.toml +++ b/src/tools/miri/Cargo.toml @@ -59,6 +59,7 @@ harness = false [features] default = ["stack-cache"] stack-cache = [] +stack-cache-consistency-check = ["stack-cache"] # Be aware that this file is inside a workspace when used via the # submodule in the rustc repo. That means there are many cargo features diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 26e55b897080c..60a1c1fa1dd2c 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -324,7 +324,7 @@ environment variable. We first document the most relevant and most commonly used number of available CPUs is `1`. Note that this flag does not affect how miri handles threads in any way. * `-Zmiri-permissive-provenance` disables the warning for integer-to-pointer casts and - [`ptr::from_exposed_addr`](https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html). + [`ptr::with_exposed_provenance`](https://doc.rust-lang.org/nightly/std/ptr/fn.with_exposed_provenance.html). This will necessarily miss some bugs as those operations are not efficiently and accurately implementable in a sanitizer, but it will only miss bugs that concern memory/pointers which is subject to these operations. @@ -507,6 +507,8 @@ binaries, and as such worth documenting: crate currently being compiled. * `MIRI_ORIG_RUSTDOC` is set and read by different phases of `cargo-miri` to remember the value of `RUSTDOC` from before it was overwritten. +* `MIRI_REPLACE_LIBRS_IF_NOT_TEST` when set to any value enables a hack that helps bootstrap + run the standard library tests in Miri. * `MIRI_VERBOSE` when set to any value tells the various `cargo-miri` phases to perform verbose logging. * `MIRI_HOST_SYSROOT` is set by bootstrap to tell `cargo-miri` which sysroot to use for *host* diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index 8c0f605fd6ec2..efa3fb0c77d1f 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -3,7 +3,7 @@ use std::env; use std::fs::{self, File}; use std::io::BufReader; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process::Command; use rustc_version::VersionMeta; @@ -89,8 +89,12 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { let verbose = num_arg_flag("-v"); // Determine the involved architectures. - let rustc_version = VersionMeta::for_command(miri_for_host()) - .expect("failed to determine underlying rustc version of Miri"); + let rustc_version = VersionMeta::for_command(miri_for_host()).unwrap_or_else(|err| { + panic!( + "failed to determine underlying rustc version of Miri ({:?}):\n{err:?}", + miri_for_host() + ) + }); let host = &rustc_version.host; let target = get_arg_flag_value("--target"); let target = target.as_ref().unwrap_or(host); @@ -179,18 +183,27 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { ); } cmd.env("RUSTC_WRAPPER", &cargo_miri_path); + // There's also RUSTC_WORKSPACE_WRAPPER, which gets in the way of our own wrapping. + if env::var_os("RUSTC_WORKSPACE_WRAPPER").is_some() { + println!( + "WARNING: Ignoring `RUSTC_WORKSPACE_WRAPPER` environment variable, Miri does not support wrapping." + ); + } + cmd.env_remove("RUSTC_WORKSPACE_WRAPPER"); // We are going to invoke `MIRI` for everything, not `RUSTC`. if env::var_os("RUSTC").is_some() && env::var_os("MIRI").is_none() { println!( "WARNING: Ignoring `RUSTC` environment variable; set `MIRI` if you want to control the binary used as the driver." ); } - // Build scripts (and also cargo: https://github.com/rust-lang/cargo/issues/10885) will invoke - // `rustc` even when `RUSTC_WRAPPER` is set. To make sure everything is coherent, we want that - // to be the Miri driver, but acting as rustc, on the target level. (Target, rather than host, - // is needed for cross-interpretation situations.) This is not a perfect emulation of real rustc - // (it might be unable to produce binaries since the sysroot is check-only), but it's as close - // as we can get, and it's good enough for autocfg. + // Ideally we would set RUSTC to some non-existent path, so we can be sure our wrapping is + // always applied. However, buggy build scripts (https://github.com/eyre-rs/eyre/issues/84) and + // also cargo (https://github.com/rust-lang/cargo/issues/10885) will invoke `rustc` even when + // `RUSTC_WRAPPER` is set, bypassing the wrapper. To make sure everything is coherent, we want + // that to be the Miri driver, but acting as rustc, on the target level. (Target, rather than + // host, is needed for cross-interpretation situations.) This is not a perfect emulation of real + // rustc (it might be unable to produce binaries since the sysroot is check-only), but it's as + // close as we can get, and it's good enough for autocfg. // // In `main`, we need the value of `RUSTC` to distinguish RUSTC_WRAPPER invocations from rustdoc // or TARGET_RUNNER invocations, so we canonicalize it here to make it exceedingly unlikely that @@ -213,7 +226,7 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { } // Run cargo. - debug_cmd("[cargo-miri miri]", verbose, &cmd); + debug_cmd("[cargo-miri cargo]", verbose, &cmd); exec(cmd) } @@ -247,6 +260,16 @@ pub fn phase_rustc(mut args: impl Iterator, phase: RustcPhase) { /// Cargo does not give us this information directly, so we need to check /// various command-line flags. fn is_runnable_crate() -> bool { + // Determine whether this is cargo invoking rustc to get some infos. Ideally we'd check "is + // there a filename passed to rustc", but that's very hard as we would have to know whether + // e.g. `--print foo` is a booolean flag `--print` followed by filename `foo` or equivalent + // to `--print=foo`. So instead we use this more fragile approach of detecting the presence + // of a "query" flag rather than the absence of a filename. + let info_query = get_arg_flag_value("--print").is_some() || has_arg_flag("-vV"); + if info_query { + // Nothing to run. + return false; + } let is_bin = get_arg_flag_value("--crate-type").as_deref().unwrap_or("bin") == "bin"; let is_test = has_arg_flag("--test"); is_bin || is_test @@ -285,16 +308,9 @@ pub fn phase_rustc(mut args: impl Iterator, phase: RustcPhase) { } } - // phase_cargo_miri set `MIRI_BE_RUSTC` for when build scripts directly invoke the driver; - // however, if we get called back by cargo here, we'll carefully compute the right flags - // ourselves, so we first un-do what the earlier phase did. - env::remove_var("MIRI_BE_RUSTC"); - let verbose = std::env::var("MIRI_VERBOSE") .map_or(0, |verbose| verbose.parse().expect("verbosity flag must be an integer")); let target_crate = is_target_crate(); - // Determine whether this is cargo invoking rustc to get some infos. - let info_query = get_arg_flag_value("--print").is_some() || has_arg_flag("-vV"); let store_json = |info: CrateRunInfo| { if get_arg_flag_value("--emit").unwrap_or_default().split(',').any(|e| e == "dep-info") { @@ -321,7 +337,7 @@ pub fn phase_rustc(mut args: impl Iterator, phase: RustcPhase) { } }; - let runnable_crate = !info_query && is_runnable_crate(); + let runnable_crate = is_runnable_crate(); if runnable_crate && target_crate { assert!( @@ -395,10 +411,26 @@ pub fn phase_rustc(mut args: impl Iterator, phase: RustcPhase) { let mut emit_link_hack = false; // Arguments are treated very differently depending on whether this crate is // for interpretation by Miri, or for use by a build script / proc macro. - if !info_query && target_crate { - // Forward arguments, but remove "link" from "--emit" to make this a check-only build. + if target_crate { + // Forward arguments, but patched. let emit_flag = "--emit"; + // This hack helps bootstrap run standard library tests in Miri. The issue is as follows: + // when running `cargo miri test` on libcore, cargo builds a local copy of core and makes it + // a dependency of the integration test crate. This copy duplicates all the lang items, so + // the build fails. (Regular testing avoids this because the sysroot is a literal copy of + // what `cargo build` produces, but since Miri builds its own sysroot this does not work for + // us.) So we need to make it so that the locally built libcore contains all the items from + // `core`, but does not re-define them -- we want to replace the entire crate but a + // re-export of the sysroot crate. We do this by swapping out the source file: if + // `MIRI_REPLACE_LIBRS_IF_NOT_TEST` is set and we are building a `lib.rs` file, and a + // `lib.miri.rs` file exists in the same folder, we build that instead. But crucially we + // only do that for the library, not the unit test crate (which would be runnable) or + // rustdoc (which would have a different `phase`). + let replace_librs = env::var_os("MIRI_REPLACE_LIBRS_IF_NOT_TEST").is_some() + && !runnable_crate + && phase == RustcPhase::Build; while let Some(arg) = args.next() { + // Patch `--emit`: remove "link" from "--emit" to make this a check-only build. if let Some(val) = arg.strip_prefix(emit_flag) { // Patch this argument. First, extract its value. let val = @@ -413,13 +445,36 @@ pub fn phase_rustc(mut args: impl Iterator, phase: RustcPhase) { } } cmd.arg(format!("{emit_flag}={}", val.join(","))); - } else if arg == "--extern" { - // Patch `--extern` filenames, since Cargo sometimes passes stub `.rlib` files: - // https://github.com/rust-lang/miri/issues/1705 + continue; + } + // Patch `--extern` filenames, since Cargo sometimes passes stub `.rlib` files: + // https://github.com/rust-lang/miri/issues/1705 + if arg == "--extern" { forward_patched_extern_arg(&mut args, &mut cmd); - } else { - cmd.arg(arg); + continue; } + // If the REPLACE_LIBRS hack is enabled and we are building a `lib.rs` file, and a + // `lib.miri.rs` file exists, then build that instead. We only consider relative paths + // as cargo uses those for files in the workspace; dependencies from crates.io get + // absolute paths. + if replace_librs { + let path = Path::new(&arg); + if path.is_relative() + && path.file_name().is_some_and(|f| f == "lib.rs") + && path.is_file() + { + let miri_rs = Path::new(&arg).with_extension("miri.rs"); + if miri_rs.is_file() { + if verbose > 0 { + eprintln!("Performing REPLACE_LIBRS hack: {arg:?} -> {miri_rs:?}"); + } + cmd.arg(miri_rs); + continue; + } + } + } + // Fallback: just propagate the argument. + cmd.arg(arg); } // During setup, patch the panic runtime for `libpanic_abort` (mirroring what bootstrap usually does). @@ -429,17 +484,14 @@ pub fn phase_rustc(mut args: impl Iterator, phase: RustcPhase) { cmd.arg("-C").arg("panic=abort"); } } else { - // For host crates (but not when we are just printing some info), - // we might still have to set the sysroot. - if !info_query { - // When we're running `cargo-miri` from `x.py` we need to pass the sysroot explicitly - // due to bootstrap complications. - if let Some(sysroot) = std::env::var_os("MIRI_HOST_SYSROOT") { - cmd.arg("--sysroot").arg(sysroot); - } + // This is a host crate. + // When we're running `cargo-miri` from `x.py` we need to pass the sysroot explicitly + // due to bootstrap complications. + if let Some(sysroot) = std::env::var_os("MIRI_HOST_SYSROOT") { + cmd.arg("--sysroot").arg(sysroot); } - // For host crates or when we are printing, just forward everything. + // Forward everything. cmd.args(args); } @@ -451,9 +503,7 @@ pub fn phase_rustc(mut args: impl Iterator, phase: RustcPhase) { // Run it. if verbose > 0 { - eprintln!( - "[cargo-miri rustc] target_crate={target_crate} runnable_crate={runnable_crate} info_query={info_query}" - ); + eprintln!("[cargo-miri rustc] target_crate={target_crate} runnable_crate={runnable_crate}"); } // Create a stub .rlib file if "link" was requested by cargo. @@ -480,11 +530,6 @@ pub enum RunnerPhase { } pub fn phase_runner(mut binary_args: impl Iterator, phase: RunnerPhase) { - // phase_cargo_miri set `MIRI_BE_RUSTC` for when build scripts directly invoke the driver; - // however, if we get called back by cargo here, we'll carefully compute the right flags - // ourselves, so we first un-do what the earlier phase did. - env::remove_var("MIRI_BE_RUSTC"); - let verbose = std::env::var("MIRI_VERBOSE") .map_or(0, |verbose| verbose.parse().expect("verbosity flag must be an integer")); @@ -513,13 +558,6 @@ pub fn phase_runner(mut binary_args: impl Iterator, phase: Runner // Set missing env vars. We prefer build-time env vars over run-time ones; see // for the kind of issue that fixes. for (name, val) in info.env { - // `CARGO_MAKEFLAGS` contains information about how to reach the jobserver, but by the time - // the program is being run, that jobserver no longer exists (cargo only runs the jobserver - // for the build portion of `cargo run`/`cargo test`). Hence we shouldn't forward this. - // Also see . - if name == "CARGO_MAKEFLAGS" { - continue; - } if let Some(old_val) = env::var_os(&name) { if old_val == val { // This one did not actually change, no need to re-set it. @@ -542,15 +580,13 @@ pub fn phase_runner(mut binary_args: impl Iterator, phase: Runner // but when we run here, cargo does not interpret the JSON any more. `--json` // then also needs to be dropped. let mut args = info.args.into_iter(); - let error_format_flag = "--error-format"; - let json_flag = "--json"; while let Some(arg) = args.next() { if arg == "--extern" { forward_patched_extern_arg(&mut args, &mut cmd); - } else if let Some(suffix) = arg.strip_prefix(error_format_flag) { + } else if let Some(suffix) = arg.strip_prefix("--error-format") { assert!(suffix.starts_with('=')); // Drop this argument. - } else if let Some(suffix) = arg.strip_prefix(json_flag) { + } else if let Some(suffix) = arg.strip_prefix("--json") { assert!(suffix.starts_with('=')); // Drop this argument. } else { @@ -589,13 +625,11 @@ pub fn phase_rustdoc(mut args: impl Iterator) { let rustdoc = env::var("MIRI_ORIG_RUSTDOC").unwrap_or("rustdoc".to_string()); let mut cmd = Command::new(rustdoc); - let extern_flag = "--extern"; - let runtool_flag = "--runtool"; while let Some(arg) = args.next() { - if arg == extern_flag { + if arg == "--extern" { // Patch --extern arguments to use *.rmeta files, since phase_cargo_rustc only creates stub *.rlib files. forward_patched_extern_arg(&mut args, &mut cmd); - } else if arg == runtool_flag { + } else if arg == "--runtool" { // An existing --runtool flag indicates cargo is running in cross-target mode, which we don't support. // Note that this is only passed when cargo is run with the unstable -Zdoctest-xcompile flag; // otherwise, we won't be called as rustdoc at all. diff --git a/src/tools/miri/cargo-miri/src/setup.rs b/src/tools/miri/cargo-miri/src/setup.rs index a98e1fcd485ea..401e9158faec5 100644 --- a/src/tools/miri/cargo-miri/src/setup.rs +++ b/src/tools/miri/cargo-miri/src/setup.rs @@ -90,13 +90,13 @@ pub fn setup( let cargo_cmd = { let mut command = cargo(); // Use Miri as rustc to build a libstd compatible with us (and use the right flags). + // We set ourselves (`cargo-miri`) instead of Miri directly to be able to patch the flags + // for `libpanic_abort` (usually this is done by bootstrap but we have to do it ourselves). + // The `MIRI_CALLED_FROM_SETUP` will mean we dispatch to `phase_setup_rustc`. // However, when we are running in bootstrap, we cannot just overwrite `RUSTC`, // because we still need bootstrap to distinguish between host and target crates. // In that case we overwrite `RUSTC_REAL` instead which determines the rustc used // for target crates. - // We set ourselves (`cargo-miri`) instead of Miri directly to be able to patch the flags - // for `libpanic_abort` (usually this is done by bootstrap but we have to do it ourselves). - // The `MIRI_CALLED_FROM_SETUP` will mean we dispatch to `phase_setup_rustc`. let cargo_miri_path = std::env::current_exe().expect("current executable path invalid"); if env::var_os("RUSTC_STAGE").is_some() { assert!(env::var_os("RUSTC").is_some()); diff --git a/src/tools/miri/cargo-miri/src/util.rs b/src/tools/miri/cargo-miri/src/util.rs index 6c1a074cd8c6e..d99957d9c2208 100644 --- a/src/tools/miri/cargo-miri/src/util.rs +++ b/src/tools/miri/cargo-miri/src/util.rs @@ -101,7 +101,12 @@ pub fn find_miri() -> PathBuf { } pub fn miri() -> Command { - Command::new(find_miri()) + let mut cmd = Command::new(find_miri()); + // We never want to inherit this from the environment. + // However, this is sometimes set in the environment to work around build scripts that don't + // honor RUSTC_WRAPPER. So remove it again in case it is set. + cmd.env_remove("MIRI_BE_RUSTC"); + cmd } pub fn miri_for_host() -> Command { diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index 54c5d3087fde1..cccf10a7d7032 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -22,17 +22,24 @@ if [ "$HOST_TARGET" = i686-pc-windows-msvc ]; then BASH="C:/Program Files/Git/usr/bin/bash" fi -# Determine configuration for installed build -echo "Installing release version of Miri" +# Global configuration export RUSTFLAGS="-D warnings" export CARGO_INCREMENTAL=0 export CARGO_EXTRA_FLAGS="--locked" + +# Determine configuration for installed build +echo "Installing release version of Miri" ./miri install -# Prepare debug build for direct `./miri` invocations -echo "Building debug version of Miri" +echo "Checking various feature flag configurations" ./miri check --no-default-features # make sure this can be built -./miri check --all-features # and this, too +./miri check # and this, too +# `--all-features` is used for the build below, so no extra check needed. + +# Prepare debug build for direct `./miri` invocations. +# We enable all features to make sure the Stacked Borrows consistency check runs. +echo "Building debug version of Miri" +export CARGO_EXTRA_FLAGS="$CARGO_EXTRA_FLAGS --all-features" ./miri build --all-targets # the build that all the `./miri test` below will use endgroup @@ -46,8 +53,8 @@ function run_tests { fi ## ui test suite - # On the host and on Linux, also stress-test the GC. - if [ -z "${MIRI_TEST_TARGET:-}" ] || [ "$HOST_TARGET" = x86_64-unknown-linux-gnu ]; then + # On the host, also stress-test the GC. + if [ -z "${MIRI_TEST_TARGET:-}" ]; then MIRIFLAGS="${MIRIFLAGS:-} -Zmiri-provenance-gc=1" ./miri test else ./miri test @@ -130,6 +137,8 @@ case $HOST_TARGET in MIRI_TEST_TARGET=aarch64-unknown-linux-gnu run_tests MIRI_TEST_TARGET=aarch64-apple-darwin run_tests MIRI_TEST_TARGET=i686-pc-windows-gnu run_tests + MIRI_TEST_TARGET=x86_64-pc-windows-gnu run_tests + MIRI_TEST_TARGET=arm-unknown-linux-gnueabi run_tests # Some targets are only partially supported. MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal hello integer vec panic/panic concurrency/simple pthread-threadname libc-getentropy libc-getrandom libc-misc libc-fs atomic env align num_cpus MIRI_TEST_TARGET=i686-unknown-freebsd run_tests_minimal hello integer vec panic/panic concurrency/simple pthread-threadname libc-getentropy libc-getrandom libc-misc libc-fs atomic env align num_cpus @@ -145,9 +154,7 @@ case $HOST_TARGET in MIRI_TEST_TARGET=x86_64-pc-windows-msvc run_tests ;; i686-pc-windows-msvc) - MIRI_TEST_TARGET=arm-unknown-linux-gnueabi run_tests MIRI_TEST_TARGET=x86_64-unknown-linux-gnu run_tests - MIRI_TEST_TARGET=x86_64-pc-windows-gnu run_tests ;; *) echo "FATAL: unknown OS" diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs index 66f323290b2b0..58deac66560d5 100644 --- a/src/tools/miri/miri-script/src/commands.rs +++ b/src/tools/miri/miri-script/src/commands.rs @@ -479,10 +479,11 @@ impl Command { Ok(()) } - fn run(dep: bool, flags: Vec) -> Result<()> { + fn run(dep: bool, mut flags: Vec) -> Result<()> { let mut e = MiriEnv::new()?; // Scan for "--target" to overwrite the "MIRI_TEST_TARGET" env var so - // that we set the MIRI_SYSROOT up the right way. + // that we set the MIRI_SYSROOT up the right way. We must make sure that + // MIRI_TEST_TARGET and `--target` are in sync. use itertools::Itertools; let target = flags .iter() @@ -493,33 +494,35 @@ impl Command { // Found it! e.sh.set_var("MIRI_TEST_TARGET", target); } else if let Ok(target) = std::env::var("MIRI_TEST_TARGET") { - // Make sure miri actually uses this target. - let miriflags = e.sh.var("MIRIFLAGS").unwrap_or_default(); - e.sh.set_var("MIRIFLAGS", format!("{miriflags} --target {target}")); + // Convert `MIRI_TEST_TARGET` into `--target`. + flags.push("--target".into()); + flags.push(target.into()); } - // Scan for "--edition" (we'll set one ourselves if that flag is not present). + // Scan for "--edition", set one ourselves if that flag is not present. let have_edition = flags.iter().take_while(|arg| *arg != "--").any(|arg| *arg == "--edition"); + if !have_edition { + flags.push("--edition=2021".into()); // keep in sync with `tests/ui.rs`.` + } // Prepare a sysroot. e.build_miri_sysroot(/* quiet */ true)?; - // Then run the actual command. + // Then run the actual command. Also add MIRIFLAGS. let miri_manifest = path!(e.miri_dir / "Cargo.toml"); let miri_flags = e.sh.var("MIRIFLAGS").unwrap_or_default(); let miri_flags = flagsplit(&miri_flags); let toolchain = &e.toolchain; let extra_flags = &e.cargo_extra_flags; - let edition_flags = (!have_edition).then_some("--edition=2021"); // keep in sync with `tests/ui.rs`.` if dep { cmd!( e.sh, - "cargo +{toolchain} --quiet test {extra_flags...} --manifest-path {miri_manifest} --test ui -- --miri-run-dep-mode {miri_flags...} {edition_flags...} {flags...}" + "cargo +{toolchain} --quiet test {extra_flags...} --manifest-path {miri_manifest} --test ui -- --miri-run-dep-mode {miri_flags...} {flags...}" ).quiet().run()?; } else { cmd!( e.sh, - "cargo +{toolchain} --quiet run {extra_flags...} --manifest-path {miri_manifest} -- {miri_flags...} {edition_flags...} {flags...}" + "cargo +{toolchain} --quiet run {extra_flags...} --manifest-path {miri_manifest} -- {miri_flags...} {flags...}" ).quiet().run()?; } Ok(()) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index fa06a069d541e..187756851c78a 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -cb7c63606e53715f94f3ba04d38e50772e4cd23d +5baf1e13f568b61e121953bf6a3d09faee7dd446 diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs index e1714aa9e46b9..fec39ec2b8e31 100644 --- a/src/tools/miri/src/alloc_addresses/mod.rs +++ b/src/tools/miri/src/alloc_addresses/mod.rs @@ -18,12 +18,12 @@ use reuse_pool::ReusePool; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ProvenanceMode { - /// We support `expose_addr`/`from_exposed_addr` via "wildcard" provenance. - /// However, we want on `from_exposed_addr` to alert the user of the precision loss. + /// We support `expose_provenance`/`with_exposed_provenance` via "wildcard" provenance. + /// However, we warn on `with_exposed_provenance` to alert the user of the precision loss. Default, /// Like `Default`, but without the warning. Permissive, - /// We error on `from_exposed_addr`, ensuring no precision loss. + /// We error on `with_exposed_provenance`, ensuring no precision loss. Strict, } diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs index 712c26a9afd7b..76430498e2bf0 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs @@ -146,7 +146,7 @@ impl<'tcx> Stack { /// Panics if any of the caching mechanisms have broken, /// - The StackCache indices don't refer to the parallel items, /// - There are no Unique items outside of first_unique..last_unique - #[cfg(all(feature = "stack-cache", debug_assertions))] + #[cfg(feature = "stack-cache-consistency-check")] fn verify_cache_consistency(&self) { // Only a full cache needs to be valid. Also see the comments in find_granting_cache // and set_unknown_bottom. @@ -190,7 +190,7 @@ impl<'tcx> Stack { tag: ProvenanceExtra, exposed_tags: &FxHashSet, ) -> Result, ()> { - #[cfg(all(feature = "stack-cache", debug_assertions))] + #[cfg(feature = "stack-cache-consistency-check")] self.verify_cache_consistency(); let ProvenanceExtra::Concrete(tag) = tag else { @@ -327,7 +327,7 @@ impl<'tcx> Stack { // This primes the cache for the next access, which is almost always the just-added tag. self.cache.add(new_idx, new); - #[cfg(debug_assertions)] + #[cfg(feature = "stack-cache-consistency-check")] self.verify_cache_consistency(); } @@ -410,7 +410,7 @@ impl<'tcx> Stack { self.unique_range.end = self.unique_range.end.min(disable_start); } - #[cfg(all(feature = "stack-cache", debug_assertions))] + #[cfg(feature = "stack-cache-consistency-check")] self.verify_cache_consistency(); Ok(()) @@ -465,7 +465,7 @@ impl<'tcx> Stack { self.unique_range = 0..0; } - #[cfg(all(feature = "stack-cache", debug_assertions))] + #[cfg(feature = "stack-cache-consistency-check")] self.verify_cache_consistency(); Ok(()) } diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs index 4394e3c2c86a4..2690bc026a120 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs @@ -365,7 +365,7 @@ type S = &'static str; /// Pretty-printing details /// /// Example: -/// ``` +/// ```rust,ignore (private type) /// DisplayFmtWrapper { /// top: '>', /// bot: '<', @@ -393,7 +393,7 @@ struct DisplayFmtWrapper { /// Formating of the permissions on each range. /// /// Example: -/// ``` +/// ```rust,ignore (private type) /// DisplayFmtPermission { /// open: "[", /// sep: "|", @@ -425,7 +425,7 @@ struct DisplayFmtPermission { /// Formating of the tree structure. /// /// Example: -/// ``` +/// ```rust,ignore (private type) /// DisplayFmtPadding { /// join_middle: "|-", /// join_last: "'-", @@ -463,7 +463,7 @@ struct DisplayFmtPadding { /// How to show whether a location has been accessed /// /// Example: -/// ``` +/// ```rust,ignore (private type) /// DisplayFmtAccess { /// yes: " ", /// no: "?", diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs index 5c7f5ea46bacb..bec51c7cdf2eb 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs @@ -181,8 +181,12 @@ impl Permission { pub fn is_initial(&self) -> bool { self.inner.is_initial() } + /// Check if `self` is the terminal state of a pointer (is `Disabled`). + pub fn is_disabled(&self) -> bool { + self.inner == Disabled + } - /// Default initial permission of the root of a new tree. + /// Default initial permission of the root of a new tree at inbounds positions. /// Must *only* be used for the root, this is not in general an "initial" permission! pub fn new_active() -> Self { Self { inner: Active } @@ -193,11 +197,17 @@ impl Permission { Self { inner: Reserved { ty_is_freeze, conflicted: false } } } - /// Default initial permission of a reborrowed shared reference + /// Default initial permission of a reborrowed shared reference. pub fn new_frozen() -> Self { Self { inner: Frozen } } + /// Default initial permission of the root of a new tre at out-of-bounds positions. + /// Must *only* be used for the root, this is not in general an "initial" permission! + pub fn new_disabled() -> Self { + Self { inner: Disabled } + } + /// Apply the transition to the inner PermissionPriv. pub fn perform_access( kind: AccessKind, diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs index dda1c7cca19a7..0fea78daa8838 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs @@ -42,6 +42,7 @@ pub(super) struct LocationState { /// protected, that is *not* the case for uninitialized locations. Instead we just have a latent /// "future initial permission" of `Disabled`, causing UB only if an access is ever actually /// performed. + /// Note that the tree root is also always initialized, as if the allocation was a write access. initialized: bool, /// This pointer's current permission / future initial permission. permission: Permission, @@ -55,17 +56,18 @@ pub(super) struct LocationState { } impl LocationState { - /// Default initial state has never been accessed and has been subjected to no - /// foreign access. - fn new(permission: Permission) -> Self { + /// Constructs a new initial state. It has neither been accessed, nor been subjected + /// to any foreign access yet. + /// The permission is not allowed to be `Active`. + fn new_uninit(permission: Permission) -> Self { + assert!(permission.is_initial() || permission.is_disabled()); Self { permission, initialized: false, latest_foreign_access: None } } - /// Record that this location was accessed through a child pointer by - /// marking it as initialized - fn with_access(mut self) -> Self { - self.initialized = true; - self + /// Constructs a new initial state. It has not yet been subjected + /// to any foreign access. However, it is already marked as having been accessed. + fn new_init(permission: Permission) -> Self { + Self { permission, initialized: true, latest_foreign_access: None } } /// Check if the location has been initialized, i.e. if it has @@ -238,8 +240,10 @@ pub(super) struct Node { /// If the pointer was reborrowed, it has children. // FIXME: bench to compare this to FxHashSet and to other SmallVec sizes pub children: SmallVec<[UniIndex; 4]>, - /// Either `Reserved` or `Frozen`, the permission this tag will be lazily initialized - /// to on the first access. + /// Either `Reserved`, `Frozen`, or `Disabled`, it is the permission this tag will + /// lazily be initialized to on the first access. + /// It is only ever `Disabled` for a tree root, since the root is initialized to `Active` by + /// its own separate mechanism. default_initial_perm: Permission, /// Some extra information useful only for debugging purposes pub debug_info: NodeDebugInfo, @@ -444,12 +448,14 @@ impl<'tree> TreeVisitor<'tree> { impl Tree { /// Create a new tree, with only a root pointer. pub fn new(root_tag: BorTag, size: Size, span: Span) -> Self { - let root_perm = Permission::new_active(); + // The root has `Disabled` as the default permission, + // so that any access out of bounds is invalid. + let root_default_perm = Permission::new_disabled(); let mut tag_mapping = UniKeyMap::default(); let root_idx = tag_mapping.insert(root_tag); let nodes = { let mut nodes = UniValMap::::default(); - let mut debug_info = NodeDebugInfo::new(root_tag, root_perm, span); + let mut debug_info = NodeDebugInfo::new(root_tag, root_default_perm, span); // name the root so that all allocations contain one named pointer debug_info.add_name("root of the allocation"); nodes.insert( @@ -458,7 +464,7 @@ impl Tree { tag: root_tag, parent: None, children: SmallVec::default(), - default_initial_perm: root_perm, + default_initial_perm: root_default_perm, debug_info, }, ); @@ -466,7 +472,11 @@ impl Tree { }; let rperms = { let mut perms = UniValMap::default(); - perms.insert(root_idx, LocationState::new(root_perm).with_access()); + // We manually set it to `Active` on all in-bounds positions. + // We also ensure that it is initalized, so that no `Active` but + // not yet initialized nodes exist. Essentially, we pretend there + // was a write that initialized these to `Active`. + perms.insert(root_idx, LocationState::new_init(Permission::new_active())); RangeMap::new(size, perms) }; Self { root: root_idx, nodes, rperms, tag_mapping } @@ -500,7 +510,7 @@ impl<'tcx> Tree { // Register new_tag as a child of parent_tag self.nodes.get_mut(parent_idx).unwrap().children.push(idx); // Initialize perms - let perm = LocationState::new(default_initial_perm).with_access(); + let perm = LocationState::new_init(default_initial_perm); for (_perms_range, perms) in self.rperms.iter_mut(reborrow_range.start, reborrow_range.size) { perms.insert(idx, perm); @@ -599,7 +609,7 @@ impl<'tcx> Tree { -> Result { let NodeAppArgs { node, mut perm, rel_pos } = args; - let old_state = perm.or_insert(LocationState::new(node.default_initial_perm)); + let old_state = perm.or_insert(LocationState::new_uninit(node.default_initial_perm)); match old_state.skip_if_known_noop(access_kind, rel_pos) { ContinueTraversal::SkipChildren => return Ok(ContinueTraversal::SkipChildren), diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs index 35f3b53afdb47..f568850d8dba0 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs @@ -516,11 +516,11 @@ mod spurious_read { let source = LocStateProtPair { xy_rel: RelPosXY::MutuallyForeign, x: LocStateProt { - state: LocationState::new(Permission::new_frozen()).with_access(), + state: LocationState::new_init(Permission::new_frozen()), prot: true, }, y: LocStateProt { - state: LocationState::new(Permission::new_reserved(false)), + state: LocationState::new_uninit(Permission::new_reserved(false)), prot: true, }, }; diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/unimap.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/unimap.rs index d9cad9c8e0bd6..f45b2d9e00a6a 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/unimap.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/unimap.rs @@ -132,7 +132,7 @@ where /// the associated `UniIndex` from ALL `UniValMap`s. /// /// Example of such behavior: - /// ``` + /// ```rust,ignore (private type can't be doctested) /// let mut keymap = UniKeyMap::::default(); /// let mut valmap = UniValMap::::default(); /// // Insert 'a' -> _ -> 'A' diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index e2e18d3a7344f..d0d73bb1b34c0 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -1046,7 +1046,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// Run the core interpreter loop. Returns only when an interrupt occurs (an error or program /// termination). fn run_threads(&mut self) -> InterpResult<'tcx, !> { - let this = self.eval_context_mut(); + let this = self.eval_context_mut(); loop { if CTRL_C_RECEIVED.load(Relaxed) { this.machine.handle_abnormal_termination(); diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 99d37065bace4..30349c003a94b 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -66,7 +66,7 @@ impl fmt::Display for TerminationInfo { Int2PtrWithStrictProvenance => write!( f, - "integer-to-pointer casts and `ptr::from_exposed_addr` are not supported with `-Zmiri-strict-provenance`" + "integer-to-pointer casts and `ptr::with_exposed_provenance` are not supported with `-Zmiri-strict-provenance`" ), StackedBorrowsUb { msg, .. } => write!(f, "{msg}"), TreeBorrowsUb { title, .. } => write!(f, "{title}"), @@ -593,7 +593,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { ( None, format!( - "This program is using integer-to-pointer casts or (equivalently) `ptr::from_exposed_addr`," + "This program is using integer-to-pointer casts or (equivalently) `ptr::with_exposed_provenance`," ), ), ( @@ -603,7 +603,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { ( None, format!( - "See https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html for more details on that operation." + "See https://doc.rust-lang.org/nightly/std/ptr/fn.with_exposed_provenance.html for more details on that operation." ), ), ( @@ -615,7 +615,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { ( None, format!( - "You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `from_exposed_addr` semantics." + "You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `with_exposed_provenance` semantics." ), ), ( diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index c12fe0e086d8c..6e320b60eecb7 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -105,24 +105,41 @@ fn try_resolve_did(tcx: TyCtxt<'_>, path: &[&str], namespace: Option) (path, None) }; - // First find the crate. - let krate = - tcx.crates(()).iter().find(|&&krate| tcx.crate_name(krate).as_str() == crate_name)?; - let mut cur_item = DefId { krate: *krate, index: CRATE_DEF_INDEX }; - // Then go over the modules. - for &segment in modules { - cur_item = find_children(tcx, cur_item, segment) - .find(|item| tcx.def_kind(item) == DefKind::Mod)?; - } - // Finally, look up the desired item in this module, if any. - match item { - Some((item_name, namespace)) => - Some( - find_children(tcx, cur_item, item_name) - .find(|item| tcx.def_kind(item).ns() == Some(namespace))?, - ), - None => Some(cur_item), + // There may be more than one crate with this name. We try them all. + // (This is particularly relevant when running `std` tests as then there are two `std` crates: + // the one in the sysroot and the one locally built by `cargo test`.) + // FIXME: can we prefer the one from the sysroot? + 'crates: for krate in + tcx.crates(()).iter().filter(|&&krate| tcx.crate_name(krate).as_str() == crate_name) + { + let mut cur_item = DefId { krate: *krate, index: CRATE_DEF_INDEX }; + // Go over the modules. + for &segment in modules { + let Some(next_item) = find_children(tcx, cur_item, segment) + .find(|item| tcx.def_kind(item) == DefKind::Mod) + else { + continue 'crates; + }; + cur_item = next_item; + } + // Finally, look up the desired item in this module, if any. + match item { + Some((item_name, namespace)) => { + let Some(item) = find_children(tcx, cur_item, item_name) + .find(|item| tcx.def_kind(item).ns() == Some(namespace)) + else { + continue 'crates; + }; + return Some(item); + } + None => { + // Just return the module. + return Some(cur_item); + } + } } + // Item not found in any of the crates with the right name. + None } /// Convert a softfloat type to its corresponding hostfloat type. @@ -968,10 +985,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn frame_in_std(&self) -> bool { let this = self.eval_context_ref(); - let Some(start_fn) = this.tcx.lang_items().start_fn() else { - // no_std situations - return false; - }; let frame = this.frame(); // Make an attempt to get at the instance of the function this is inlined from. let instance: Option<_> = try { @@ -982,13 +995,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }; // Fall back to the instance of the function itself. let instance = instance.unwrap_or(frame.instance); - // Now check if this is in the same crate as start_fn. - // As a special exception we also allow unit tests from - // to call these - // shims. + // Now check the crate it is in. We could try to be clever here and e.g. check if this is + // the same crate as `start_fn`, but that would not work for running std tests in Miri, so + // we'd need some more hacks anyway. So we just check the name of the crate. If someone + // calls their crate `std` then we'll just let them keep the pieces. let frame_crate = this.tcx.def_path(instance.def_id()).krate; - frame_crate == this.tcx.def_path(start_fn).krate - || this.tcx.crate_name(frame_crate).as_str() == "std_miri_test" + let crate_name = this.tcx.crate_name(frame_crate); + let crate_name = crate_name.as_str(); + // On miri-test-libstd, the name of the crate is different. + crate_name == "std" || crate_name == "std_miri_test" } /// Handler that should be called when unsupported functionality is encountered. diff --git a/src/tools/miri/src/shims/intrinsics/simd.rs b/src/tools/miri/src/shims/intrinsics/simd.rs index 6973c0e9c3571..a2fc4f0f761af 100644 --- a/src/tools/miri/src/shims/intrinsics/simd.rs +++ b/src/tools/miri/src/shims/intrinsics/simd.rs @@ -514,7 +514,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { dest.transmute(this.machine.layouts.uint(dest.layout.size).unwrap(), this)?; this.write_int(res, &dest)?; } - "cast" | "as" | "cast_ptr" | "expose_addr" | "from_exposed_addr" => { + "cast" | "as" | "cast_ptr" | "expose_provenance" | "with_exposed_provenance" => { let [op] = check_arg_count(args)?; let (op, op_len) = this.operand_to_simd(op)?; let (dest, dest_len) = this.mplace_to_simd(dest)?; @@ -524,8 +524,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let unsafe_cast = intrinsic_name == "cast"; let safe_cast = intrinsic_name == "as"; let ptr_cast = intrinsic_name == "cast_ptr"; - let expose_cast = intrinsic_name == "expose_addr"; - let from_exposed_cast = intrinsic_name == "from_exposed_addr"; + let expose_cast = intrinsic_name == "expose_provenance"; + let from_exposed_cast = intrinsic_name == "with_exposed_provenance"; for i in 0..dest_len { let op = this.read_immediate(&this.project_index(&op, i)?)?; @@ -557,9 +557,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.ptr_to_ptr(&op, dest.layout)?, // Ptr/Int casts (ty::RawPtr(..), ty::Int(_) | ty::Uint(_)) if expose_cast => - this.pointer_expose_address_cast(&op, dest.layout)?, + this.pointer_expose_provenance_cast(&op, dest.layout)?, (ty::Int(_) | ty::Uint(_), ty::RawPtr(..)) if from_exposed_cast => - this.pointer_from_exposed_address_cast(&op, dest.layout)?, + this.pointer_with_exposed_provenance_cast(&op, dest.layout)?, // Error otherwise _ => throw_unsup_format!( diff --git a/src/tools/miri/src/shims/panic.rs b/src/tools/miri/src/shims/panic.rs index 34b6481dd387d..bb31ef733cf94 100644 --- a/src/tools/miri/src/shims/panic.rs +++ b/src/tools/miri/src/shims/panic.rs @@ -256,8 +256,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } _ => { - // Forward everything else to `panic` lang item. - this.start_panic(msg.description(), unwind)?; + // Call the lang item associated with this message. + let fn_item = this.tcx.require_lang_item(msg.panic_function(), None); + let instance = ty::Instance::mono(this.tcx.tcx, fn_item); + this.call_function( + instance, + Abi::Rust, + &[], + None, + StackPopCleanup::Goto { ret: None, unwind }, + )?; } } Ok(()) diff --git a/src/tools/miri/test-cargo-miri/Cargo.lock b/src/tools/miri/test-cargo-miri/Cargo.lock index f75d68f4e2983..4783f79ea8fb4 100644 --- a/src/tools/miri/test-cargo-miri/Cargo.lock +++ b/src/tools/miri/test-cargo-miri/Cargo.lock @@ -2,17 +2,11 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "anyhow" -version = "1.0.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" - [[package]] name = "autocfg" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "byteorder" @@ -22,33 +16,33 @@ checksum = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cargo-miri-test" version = "0.1.0" dependencies = [ - "anyhow", "autocfg", "byteorder 0.5.3", - "byteorder 1.4.3", + "byteorder 1.5.0", "cdylib", "exported_symbol", + "eyre", "issue_1567", "issue_1691", "issue_1705", - "issue_1760", "issue_rust_86261", - "serde_derive", + "proc-macro2", + "proc_macro_crate", ] [[package]] name = "cdylib" version = "0.1.0" dependencies = [ - "byteorder 1.4.3", + "byteorder 1.5.0", ] [[package]] @@ -62,11 +56,27 @@ dependencies = [ name = "exported_symbol_dep" version = "0.1.0" +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + [[package]] name = "issue_1567" version = "0.1.0" dependencies = [ - "byteorder 1.4.3", + "byteorder 1.5.0", ] [[package]] @@ -77,66 +87,44 @@ version = "0.1.0" name = "issue_1705" version = "0.1.0" dependencies = [ - "byteorder 1.4.3", + "byteorder 1.5.0", ] -[[package]] -name = "issue_1760" -version = "0.1.0" - [[package]] name = "issue_rust_86261" version = "0.1.0" [[package]] -name = "proc-macro2" -version = "1.0.66" +name = "once_cell" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" -dependencies = [ - "unicode-ident", -] +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] -name = "quote" -version = "1.0.33" +name = "proc-macro2" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ - "proc-macro2", + "unicode-ident", ] [[package]] -name = "serde_derive" -version = "1.0.185" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc59dfdcbad1437773485e0367fea4b090a2e0a16d9ffc46af47764536a298ec" +name = "proc_macro_crate" +version = "0.1.0" dependencies = [ "proc-macro2", - "quote", - "syn", ] [[package]] name = "subcrate" version = "0.1.0" dependencies = [ - "byteorder 1.4.3", -] - -[[package]] -name = "syn" -version = "2.0.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "byteorder 1.5.0", ] [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/src/tools/miri/test-cargo-miri/Cargo.toml b/src/tools/miri/test-cargo-miri/Cargo.toml index 58c451741bb50..bfef388669d17 100644 --- a/src/tools/miri/test-cargo-miri/Cargo.toml +++ b/src/tools/miri/test-cargo-miri/Cargo.toml @@ -12,19 +12,21 @@ edition = "2018" byteorder = "1.0" cdylib = { path = "cdylib" } exported_symbol = { path = "exported-symbol" } +proc_macro_crate = { path = "proc-macro-crate" } issue_1567 = { path = "issue-1567" } issue_1691 = { path = "issue-1691" } issue_1705 = { path = "issue-1705" } -issue_1760 = { path = "issue-1760" } issue_rust_86261 = { path = "issue-rust-86261" } [dev-dependencies] byteorder_2 = { package = "byteorder", version = "0.5" } # to test dev-dependencies behave as expected, with renaming -# Not actually used, but exercises some unique code path (`--extern` .so file). -serde_derive = "1.0.185" -# Not actually used, but uses a custom build probe so let's make sure that works. +## More dependencies that we don't actually use, but add just for extra test coverage. +# These use custom build probes, let's make sure they don't explode. # (Ideally we'd check if the probe was successful, but that's not easily possible.) -anyhow = "1.0" +# proc-macro2 is extra exciting because it is both a host-dependency (of proc_macro_crate above) +# and a target-dependency. +proc-macro2 = "1.0" +eyre = "0.6" [build-dependencies] autocfg = "1" diff --git a/src/tools/miri/test-cargo-miri/issue-1760/Cargo.toml b/src/tools/miri/test-cargo-miri/issue-1760/Cargo.toml deleted file mode 100644 index 80925c7474638..0000000000000 --- a/src/tools/miri/test-cargo-miri/issue-1760/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "issue_1760" -version = "0.1.0" -authors = ["Miri Team"] -edition = "2018" - -[lib] -proc-macro = true diff --git a/src/tools/miri/test-cargo-miri/proc-macro-crate/Cargo.toml b/src/tools/miri/test-cargo-miri/proc-macro-crate/Cargo.toml new file mode 100644 index 0000000000000..89652f9b04286 --- /dev/null +++ b/src/tools/miri/test-cargo-miri/proc-macro-crate/Cargo.toml @@ -0,0 +1,13 @@ +[package] +# regression test for issue 1760 +name = "proc_macro_crate" +version = "0.1.0" +authors = ["Miri Team"] +edition = "2018" + +[lib] +proc-macro = true + +[dependencies] +# A common dependency of proc macros, let's make sure that works. +proc-macro2 = "1.0" diff --git a/src/tools/miri/test-cargo-miri/issue-1760/build.rs b/src/tools/miri/test-cargo-miri/proc-macro-crate/build.rs similarity index 100% rename from src/tools/miri/test-cargo-miri/issue-1760/build.rs rename to src/tools/miri/test-cargo-miri/proc-macro-crate/build.rs diff --git a/src/tools/miri/test-cargo-miri/issue-1760/src/lib.rs b/src/tools/miri/test-cargo-miri/proc-macro-crate/src/lib.rs similarity index 100% rename from src/tools/miri/test-cargo-miri/issue-1760/src/lib.rs rename to src/tools/miri/test-cargo-miri/proc-macro-crate/src/lib.rs diff --git a/src/tools/miri/test-cargo-miri/src/lib.rs b/src/tools/miri/test-cargo-miri/src/lib.rs index e6b8c4ef65b2f..003341d0974ce 100644 --- a/src/tools/miri/test-cargo-miri/src/lib.rs +++ b/src/tools/miri/test-cargo-miri/src/lib.rs @@ -28,9 +28,9 @@ /// ``` #[no_mangle] pub fn make_true() -> bool { + proc_macro_crate::use_the_dependency!(); issue_1567::use_the_dependency(); issue_1705::use_the_dependency(); - issue_1760::use_the_dependency!(); issue_1691::use_me() } diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_add1.rs b/src/tools/miri/tests/fail/intrinsics/unchecked_add1.rs index 13265d0fb0ee4..3f8b4e55151ec 100644 --- a/src/tools/miri/tests/fail/intrinsics/unchecked_add1.rs +++ b/src/tools/miri/tests/fail/intrinsics/unchecked_add1.rs @@ -1,5 +1,3 @@ -#![feature(unchecked_math)] - fn main() { // MAX overflow let _val = unsafe { 40000u16.unchecked_add(30000) }; //~ ERROR: overflow executing `unchecked_add` diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_add2.rs b/src/tools/miri/tests/fail/intrinsics/unchecked_add2.rs index 229f50321d7df..3283dbf8e7ef3 100644 --- a/src/tools/miri/tests/fail/intrinsics/unchecked_add2.rs +++ b/src/tools/miri/tests/fail/intrinsics/unchecked_add2.rs @@ -1,5 +1,3 @@ -#![feature(unchecked_math)] - fn main() { // MIN overflow let _val = unsafe { (-30000i16).unchecked_add(-8000) }; //~ ERROR: overflow executing `unchecked_add` diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_mul1.rs b/src/tools/miri/tests/fail/intrinsics/unchecked_mul1.rs index 810d3418dc8fe..2feed7759ec1c 100644 --- a/src/tools/miri/tests/fail/intrinsics/unchecked_mul1.rs +++ b/src/tools/miri/tests/fail/intrinsics/unchecked_mul1.rs @@ -1,4 +1,3 @@ -#![feature(unchecked_math)] fn main() { // MAX overflow let _val = unsafe { 300u16.unchecked_mul(250u16) }; //~ ERROR: overflow executing `unchecked_mul` diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_mul2.rs b/src/tools/miri/tests/fail/intrinsics/unchecked_mul2.rs index 421019542a95a..42cd509404a55 100644 --- a/src/tools/miri/tests/fail/intrinsics/unchecked_mul2.rs +++ b/src/tools/miri/tests/fail/intrinsics/unchecked_mul2.rs @@ -1,4 +1,3 @@ -#![feature(unchecked_math)] fn main() { // MIN overflow let _val = unsafe { 1_000_000_000i32.unchecked_mul(-4) }; //~ ERROR: overflow executing `unchecked_mul` diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_sub1.rs b/src/tools/miri/tests/fail/intrinsics/unchecked_sub1.rs index c6e0066674413..e5178bf4effaa 100644 --- a/src/tools/miri/tests/fail/intrinsics/unchecked_sub1.rs +++ b/src/tools/miri/tests/fail/intrinsics/unchecked_sub1.rs @@ -1,4 +1,3 @@ -#![feature(unchecked_math)] fn main() { // MIN overflow let _val = unsafe { 14u32.unchecked_sub(22) }; //~ ERROR: overflow executing `unchecked_sub` diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_sub2.rs b/src/tools/miri/tests/fail/intrinsics/unchecked_sub2.rs index 65aa292e212da..ac9fd1e5d0622 100644 --- a/src/tools/miri/tests/fail/intrinsics/unchecked_sub2.rs +++ b/src/tools/miri/tests/fail/intrinsics/unchecked_sub2.rs @@ -1,4 +1,3 @@ -#![feature(unchecked_math)] fn main() { // MAX overflow let _val = unsafe { 30000i16.unchecked_sub(-7000) }; //~ ERROR: overflow executing `unchecked_sub` diff --git a/src/tools/miri/tests/fail/provenance/ptr_int_unexposed.rs b/src/tools/miri/tests/fail/provenance/ptr_int_unexposed.rs index 20fd330699890..f89378fcb3c4c 100644 --- a/src/tools/miri/tests/fail/provenance/ptr_int_unexposed.rs +++ b/src/tools/miri/tests/fail/provenance/ptr_int_unexposed.rs @@ -7,6 +7,6 @@ fn main() { let x_usize: usize = x_ptr.addr(); // Cast back an address that did *not* get exposed. - let ptr = std::ptr::from_exposed_addr::(x_usize); + let ptr = std::ptr::with_exposed_provenance::(x_usize); assert_eq!(unsafe { *ptr }, 3); //~ ERROR: is a dangling pointer } diff --git a/src/tools/miri/tests/fail/provenance/ptr_invalid.rs b/src/tools/miri/tests/fail/provenance/ptr_invalid.rs index 730859684a0ce..512473cd89450 100644 --- a/src/tools/miri/tests/fail/provenance/ptr_invalid.rs +++ b/src/tools/miri/tests/fail/provenance/ptr_invalid.rs @@ -4,6 +4,6 @@ fn main() { let x = 42; let xptr = &x as *const i32; - let xptr_invalid = std::ptr::without_provenance::(xptr.expose_addr()); + let xptr_invalid = std::ptr::without_provenance::(xptr.expose_provenance()); let _val = unsafe { *xptr_invalid }; //~ ERROR: is a dangling pointer } diff --git a/src/tools/miri/tests/fail/provenance/strict_provenance_cast.rs b/src/tools/miri/tests/fail/provenance/strict_provenance_cast.rs index 106cf4d804b41..d7b54f640f653 100644 --- a/src/tools/miri/tests/fail/provenance/strict_provenance_cast.rs +++ b/src/tools/miri/tests/fail/provenance/strict_provenance_cast.rs @@ -3,5 +3,5 @@ fn main() { let addr = &0 as *const i32 as usize; - let _ptr = std::ptr::from_exposed_addr::(addr); //~ ERROR: integer-to-pointer casts and `ptr::from_exposed_addr` are not supported + let _ptr = std::ptr::with_exposed_provenance::(addr); //~ ERROR: integer-to-pointer casts and `ptr::with_exposed_provenance` are not supported } diff --git a/src/tools/miri/tests/fail/provenance/strict_provenance_cast.stderr b/src/tools/miri/tests/fail/provenance/strict_provenance_cast.stderr index a110ed4ebb218..8c61b66ac4693 100644 --- a/src/tools/miri/tests/fail/provenance/strict_provenance_cast.stderr +++ b/src/tools/miri/tests/fail/provenance/strict_provenance_cast.stderr @@ -1,8 +1,8 @@ -error: unsupported operation: integer-to-pointer casts and `ptr::from_exposed_addr` are not supported with `-Zmiri-strict-provenance` +error: unsupported operation: integer-to-pointer casts and `ptr::with_exposed_provenance` are not supported with `-Zmiri-strict-provenance` --> $DIR/strict_provenance_cast.rs:LL:CC | -LL | let _ptr = std::ptr::from_exposed_addr::(addr); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ integer-to-pointer casts and `ptr::from_exposed_addr` are not supported with `-Zmiri-strict-provenance` +LL | let _ptr = std::ptr::with_exposed_provenance::(addr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ integer-to-pointer casts and `ptr::with_exposed_provenance` are not supported with `-Zmiri-strict-provenance` | = help: use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead = note: BACKTRACE: diff --git a/src/tools/miri/tests/fail/stacked_borrows/exposed_only_ro.rs b/src/tools/miri/tests/fail/stacked_borrows/exposed_only_ro.rs index b0e4cceb98f36..608ab71891942 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/exposed_only_ro.rs +++ b/src/tools/miri/tests/fail/stacked_borrows/exposed_only_ro.rs @@ -6,7 +6,7 @@ fn main() { let mut x = 0; let _fool = &mut x as *mut i32; // this would have fooled the old untagged pointer logic - let addr = (&x as *const i32).expose_addr(); - let ptr = std::ptr::from_exposed_addr_mut::(addr); + let addr = (&x as *const i32).expose_provenance(); + let ptr = std::ptr::with_exposed_provenance_mut::(addr); unsafe { *ptr = 0 }; //~ ERROR: /write access using .* no exposed tags have suitable permission in the borrow stack/ } diff --git a/src/tools/miri/tests/pass-dep/shims/posix_memalign.rs b/src/tools/miri/tests/pass-dep/shims/posix_memalign.rs index 5cf62995fbee2..db66b21341641 100644 --- a/src/tools/miri/tests/pass-dep/shims/posix_memalign.rs +++ b/src/tools/miri/tests/pass-dep/shims/posix_memalign.rs @@ -1,6 +1,6 @@ //@ignore-target-windows: No libc on Windows -#![feature(pointer_is_aligned)] +#![feature(pointer_is_aligned_to)] #![feature(strict_provenance)] use core::ptr; diff --git a/src/tools/miri/tests/pass/async-closure-captures.rs b/src/tools/miri/tests/pass/async-closure-captures.rs new file mode 100644 index 0000000000000..cac26bfe14621 --- /dev/null +++ b/src/tools/miri/tests/pass/async-closure-captures.rs @@ -0,0 +1,125 @@ +// Same as rustc's `tests/ui/async-await/async-closures/captures.rs`, keep in sync + +#![feature(async_closure, noop_waker)] + +use std::future::Future; +use std::pin::pin; +use std::task::*; + +pub fn block_on(fut: impl Future) -> T { + let mut fut = pin!(fut); + let ctx = &mut Context::from_waker(Waker::noop()); + + loop { + match fut.as_mut().poll(ctx) { + Poll::Pending => {} + Poll::Ready(t) => break t, + } + } +} + +fn main() { + block_on(async_main()); +} + +async fn call(f: &impl async Fn() -> T) -> T { + f().await +} + +async fn call_once(f: impl async FnOnce() -> T) -> T { + f().await +} + +#[derive(Debug)] +#[allow(unused)] +struct Hello(i32); + +async fn async_main() { + // Capture something by-ref + { + let x = Hello(0); + let c = async || { + println!("{x:?}"); + }; + call(&c).await; + call_once(c).await; + + let x = &Hello(1); + let c = async || { + println!("{x:?}"); + }; + call(&c).await; + call_once(c).await; + } + + // Capture something and consume it (force to `AsyncFnOnce`) + { + let x = Hello(2); + let c = async || { + println!("{x:?}"); + drop(x); + }; + call_once(c).await; + } + + // Capture something with `move`, don't consume it + { + let x = Hello(3); + let c = async move || { + println!("{x:?}"); + }; + call(&c).await; + call_once(c).await; + + let x = &Hello(4); + let c = async move || { + println!("{x:?}"); + }; + call(&c).await; + call_once(c).await; + } + + // Capture something with `move`, also consume it (so `AsyncFnOnce`) + { + let x = Hello(5); + let c = async move || { + println!("{x:?}"); + drop(x); + }; + call_once(c).await; + } + + fn force_fnonce(f: impl async FnOnce() -> T) -> impl async FnOnce() -> T { + f + } + + // Capture something with `move`, but infer to `AsyncFnOnce` + { + let x = Hello(6); + let c = force_fnonce(async move || { + println!("{x:?}"); + }); + call_once(c).await; + + let x = &Hello(7); + let c = force_fnonce(async move || { + println!("{x:?}"); + }); + call_once(c).await; + } + + // Capture something by-ref, but infer to `AsyncFnOnce` + { + let x = Hello(8); + let c = force_fnonce(async || { + println!("{x:?}"); + }); + call_once(c).await; + + let x = &Hello(9); + let c = force_fnonce(async || { + println!("{x:?}"); + }); + call_once(c).await; + } +} diff --git a/src/tools/miri/tests/pass/async-closure-captures.stdout b/src/tools/miri/tests/pass/async-closure-captures.stdout new file mode 100644 index 0000000000000..42a7999b2dcdd --- /dev/null +++ b/src/tools/miri/tests/pass/async-closure-captures.stdout @@ -0,0 +1,14 @@ +Hello(0) +Hello(0) +Hello(1) +Hello(1) +Hello(2) +Hello(3) +Hello(3) +Hello(4) +Hello(4) +Hello(5) +Hello(6) +Hello(7) +Hello(8) +Hello(9) diff --git a/src/tools/miri/tests/pass/box.stack.stderr b/src/tools/miri/tests/pass/box.stack.stderr index f6e208cea9a82..1a4d52ee3146f 100644 --- a/src/tools/miri/tests/pass/box.stack.stderr +++ b/src/tools/miri/tests/pass/box.stack.stderr @@ -4,11 +4,11 @@ warning: integer-to-pointer cast LL | let r2 = ((r as usize) + 0) as *mut i32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ integer-to-pointer cast | - = help: This program is using integer-to-pointer casts or (equivalently) `ptr::from_exposed_addr`, + = help: This program is using integer-to-pointer casts or (equivalently) `ptr::with_exposed_provenance`, = help: which means that Miri might miss pointer bugs in this program. - = help: See https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html for more details on that operation. + = help: See https://doc.rust-lang.org/nightly/std/ptr/fn.with_exposed_provenance.html for more details on that operation. = help: To ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead. - = help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `from_exposed_addr` semantics. + = help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `with_exposed_provenance` semantics. = help: Alternatively, the `-Zmiri-permissive-provenance` flag disables this warning. = note: BACKTRACE: = note: inside `into_raw` at $DIR/box.rs:LL:CC diff --git a/src/tools/miri/tests/pass/extern_types.stack.stderr b/src/tools/miri/tests/pass/extern_types.stack.stderr index 2e18f69305896..275d718129b4e 100644 --- a/src/tools/miri/tests/pass/extern_types.stack.stderr +++ b/src/tools/miri/tests/pass/extern_types.stack.stderr @@ -4,11 +4,11 @@ warning: integer-to-pointer cast LL | let x: &Foo = unsafe { &*(16 as *const Foo) }; | ^^^^^^^^^^^^^^^^^^ integer-to-pointer cast | - = help: This program is using integer-to-pointer casts or (equivalently) `ptr::from_exposed_addr`, + = help: This program is using integer-to-pointer casts or (equivalently) `ptr::with_exposed_provenance`, = help: which means that Miri might miss pointer bugs in this program. - = help: See https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html for more details on that operation. + = help: See https://doc.rust-lang.org/nightly/std/ptr/fn.with_exposed_provenance.html for more details on that operation. = help: To ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead. - = help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `from_exposed_addr` semantics. + = help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `with_exposed_provenance` semantics. = help: Alternatively, the `-Zmiri-permissive-provenance` flag disables this warning. = note: BACKTRACE: = note: inside `main` at $DIR/extern_types.rs:LL:CC diff --git a/src/tools/miri/tests/pass/portable-simd-ptrs.rs b/src/tools/miri/tests/pass/portable-simd-ptrs.rs index 70ba5636c600e..096ec78da1a0e 100644 --- a/src/tools/miri/tests/pass/portable-simd-ptrs.rs +++ b/src/tools/miri/tests/pass/portable-simd-ptrs.rs @@ -7,6 +7,6 @@ use std::simd::prelude::*; fn main() { // Pointer casts let _val: Simd<*const u8, 4> = Simd::<*const i32, 4>::splat(ptr::null()).cast(); - let addrs = Simd::<*const i32, 4>::splat(ptr::null()).expose_addr(); - let _ptrs = Simd::<*const i32, 4>::from_exposed_addr(addrs); + let addrs = Simd::<*const i32, 4>::splat(ptr::null()).expose_provenance(); + let _ptrs = Simd::<*const i32, 4>::with_exposed_provenance(addrs); } diff --git a/src/tools/miri/tests/pass/ptr_int_from_exposed.rs b/src/tools/miri/tests/pass/ptr_int_from_exposed.rs index d8d57679e6b36..5690d7865bbc3 100644 --- a/src/tools/miri/tests/pass/ptr_int_from_exposed.rs +++ b/src/tools/miri/tests/pass/ptr_int_from_exposed.rs @@ -10,9 +10,9 @@ fn ptr_roundtrip_out_of_bounds() { let x: i32 = 3; let x_ptr = &x as *const i32; - let x_usize = x_ptr.wrapping_offset(128).expose_addr(); + let x_usize = x_ptr.wrapping_offset(128).expose_provenance(); - let ptr = ptr::from_exposed_addr::(x_usize).wrapping_offset(-128); + let ptr = ptr::with_exposed_provenance::(x_usize).wrapping_offset(-128); assert_eq!(unsafe { *ptr }, 3); } @@ -24,10 +24,10 @@ fn ptr_roundtrip_confusion() { let x_ptr = &x as *const i32; let y_ptr = &y as *const i32; - let x_usize = x_ptr.expose_addr(); - let y_usize = y_ptr.expose_addr(); + let x_usize = x_ptr.expose_provenance(); + let y_usize = y_ptr.expose_provenance(); - let ptr = ptr::from_exposed_addr::(y_usize); + let ptr = ptr::with_exposed_provenance::(y_usize); let ptr = ptr.with_addr(x_usize); assert_eq!(unsafe { *ptr }, 0); } @@ -37,9 +37,9 @@ fn ptr_roundtrip_imperfect() { let x: u8 = 3; let x_ptr = &x as *const u8; - let x_usize = x_ptr.expose_addr() + 128; + let x_usize = x_ptr.expose_provenance() + 128; - let ptr = ptr::from_exposed_addr::(x_usize).wrapping_offset(-128); + let ptr = ptr::with_exposed_provenance::(x_usize).wrapping_offset(-128); assert_eq!(unsafe { *ptr }, 3); } @@ -48,10 +48,10 @@ fn ptr_roundtrip_null() { let x = &42; let x_ptr = x as *const i32; let x_null_ptr = x_ptr.with_addr(0); // addr 0, but still the provenance of x - let null = x_null_ptr.expose_addr(); + let null = x_null_ptr.expose_provenance(); assert_eq!(null, 0); - let x_null_ptr_copy = ptr::from_exposed_addr::(null); // just a roundtrip, so has provenance of x (angelically) + let x_null_ptr_copy = ptr::with_exposed_provenance::(null); // just a roundtrip, so has provenance of x (angelically) let x_ptr_copy = x_null_ptr_copy.with_addr(x_ptr.addr()); // addr of x and provenance of x assert_eq!(unsafe { *x_ptr_copy }, 42); } diff --git a/src/tools/miri/tests/pass/stacked-borrows/int-to-ptr.rs b/src/tools/miri/tests/pass/stacked-borrows/int-to-ptr.rs index e467356dd04ba..c89d79b42e31e 100644 --- a/src/tools/miri/tests/pass/stacked-borrows/int-to-ptr.rs +++ b/src/tools/miri/tests/pass/stacked-borrows/int-to-ptr.rs @@ -17,7 +17,7 @@ fn example(variant: bool) { unsafe { fn not_so_innocent(x: &mut u32) -> usize { let x_raw4 = x as *mut u32; - x_raw4.expose_addr() + x_raw4.expose_provenance() } let mut c = 42u32; @@ -26,7 +26,7 @@ fn example(variant: bool) { // stack: [..., Unique(1)] let x_raw2 = x_unique1 as *mut u32; - let x_raw2_addr = x_raw2.expose_addr(); + let x_raw2_addr = x_raw2.expose_provenance(); // stack: [..., Unique(1), SharedRW(2)] let x_unique3 = &mut *x_raw2; @@ -39,7 +39,7 @@ fn example(variant: bool) { // 4 is the "obvious" choice (topmost tag, what we used to do with untagged pointers). // And indeed if `variant == true` it is the only possible choice. // But if `variant == false` then 2 is the only possible choice! - let x_wildcard = ptr::from_exposed_addr_mut::(x_raw2_addr); + let x_wildcard = ptr::with_exposed_provenance_mut::(x_raw2_addr); if variant { // If we picked 2, this will invalidate 3. diff --git a/src/tools/miri/tests/pass/stacked-borrows/issue-miri-2389.stderr b/src/tools/miri/tests/pass/stacked-borrows/issue-miri-2389.stderr index f3ba052ae5130..7cbfad3942b32 100644 --- a/src/tools/miri/tests/pass/stacked-borrows/issue-miri-2389.stderr +++ b/src/tools/miri/tests/pass/stacked-borrows/issue-miri-2389.stderr @@ -4,11 +4,11 @@ warning: integer-to-pointer cast LL | let wildcard = &root0 as *const Cell as usize as *const Cell; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ integer-to-pointer cast | - = help: This program is using integer-to-pointer casts or (equivalently) `ptr::from_exposed_addr`, + = help: This program is using integer-to-pointer casts or (equivalently) `ptr::with_exposed_provenance`, = help: which means that Miri might miss pointer bugs in this program. - = help: See https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html for more details on that operation. + = help: See https://doc.rust-lang.org/nightly/std/ptr/fn.with_exposed_provenance.html for more details on that operation. = help: To ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead. - = help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `from_exposed_addr` semantics. + = help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `with_exposed_provenance` semantics. = help: Alternatively, the `-Zmiri-permissive-provenance` flag disables this warning. = note: BACKTRACE: = note: inside `main` at $DIR/issue-miri-2389.rs:LL:CC diff --git a/src/tools/miri/tests/pass/stacked-borrows/unknown-bottom-gc.rs b/src/tools/miri/tests/pass/stacked-borrows/unknown-bottom-gc.rs index 5bb4e879c3e44..55356814a1a03 100644 --- a/src/tools/miri/tests/pass/stacked-borrows/unknown-bottom-gc.rs +++ b/src/tools/miri/tests/pass/stacked-borrows/unknown-bottom-gc.rs @@ -9,7 +9,7 @@ fn main() { // Expose the allocation and use the exposed pointer, creating an unknown bottom unsafe { - let p: *mut u8 = ptr::from_exposed_addr::(ptr.expose_addr()) as *mut u8; + let p: *mut u8 = ptr::with_exposed_provenance::(ptr.expose_provenance()) as *mut u8; *p = 1; } diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index 129d1dfd73260..a75fa4cf986da 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -54,34 +54,13 @@ fn build_so_for_c_ffi_tests() -> PathBuf { so_file_path } -fn test_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) -> Config { +/// Does *not* set any args or env vars, since it is shared between the test runner and +/// run_dep_mode. +fn miri_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) -> Config { // Miri is rustc-like, so we create a default builder for rustc and modify it let mut program = CommandBuilder::rustc(); program.program = miri_path(); - // Add some flags we always want. - program.args.push("-Dwarnings".into()); - program.args.push("-Dunused".into()); - program.args.push("-Ainternal_features".into()); - if let Ok(extra_flags) = env::var("MIRIFLAGS") { - for flag in extra_flags.split_whitespace() { - program.args.push(flag.into()); - } - } - program.args.push("-Zui-testing".into()); - program.args.push("--target".into()); - program.args.push(target.into()); - - // If we're on linux, and we're testing the extern-so functionality, - // then build the shared object file for testing external C function calls - // and push the relevant compiler flag. - if cfg!(target_os = "linux") && path.starts_with("tests/extern-so/") { - let so_file_path = build_so_for_c_ffi_tests(); - let mut flag = std::ffi::OsString::from("-Zmiri-extern-so-file="); - flag.push(so_file_path.into_os_string()); - program.args.push(flag); - } - let mut config = Config { target: Some(target.to_owned()), stderr_filters: STDERR.clone(), @@ -119,17 +98,38 @@ fn run_tests( with_dependencies: bool, tmpdir: &Path, ) -> Result<()> { - let mut config = test_config(target, path, mode, with_dependencies); + let mut config = miri_config(target, path, mode, with_dependencies); // Add a test env var to do environment communication tests. config.program.envs.push(("MIRI_ENV_VAR_TEST".into(), Some("0".into()))); - // Let the tests know where to store temp files (they might run for a different target, which can make this hard to find). config.program.envs.push(("MIRI_TEMP".into(), Some(tmpdir.to_owned().into()))); - // If a test ICEs, we want to see a backtrace. config.program.envs.push(("RUST_BACKTRACE".into(), Some("1".into()))); + // Add some flags we always want. + config.program.args.push("-Dwarnings".into()); + config.program.args.push("-Dunused".into()); + config.program.args.push("-Ainternal_features".into()); + if let Ok(extra_flags) = env::var("MIRIFLAGS") { + for flag in extra_flags.split_whitespace() { + config.program.args.push(flag.into()); + } + } + config.program.args.push("-Zui-testing".into()); + config.program.args.push("--target".into()); + config.program.args.push(target.into()); + + // If we're on linux, and we're testing the extern-so functionality, + // then build the shared object file for testing external C function calls + // and push the relevant compiler flag. + if cfg!(target_os = "linux") && path.starts_with("tests/extern-so/") { + let so_file_path = build_so_for_c_ffi_tests(); + let mut flag = std::ffi::OsString::from("-Zmiri-extern-so-file="); + flag.push(so_file_path.into_os_string()); + config.program.args.push(flag); + } + // Handle command-line arguments. let args = ui_test::Args::test()?; let default_bless = env::var_os("RUSTC_BLESS").is_some_and(|v| v != "0"); @@ -292,13 +292,12 @@ fn main() -> Result<()> { fn run_dep_mode(target: String, mut args: impl Iterator) -> Result<()> { let path = args.next().expect("./miri run-dep must be followed by a file name"); - let mut config = test_config( + let config = miri_config( &target, "", Mode::Yolo { rustfix: RustfixMode::Disabled }, /* with dependencies */ true, ); - config.program.args.clear(); // We want to give the user full control over flags let dep_args = config.build_dependencies()?; let mut cmd = config.program.build(&config.out_dir); diff --git a/src/tools/run-make-support/src/cc.rs b/src/tools/run-make-support/src/cc.rs new file mode 100644 index 0000000000000..2c9ad4f17006c --- /dev/null +++ b/src/tools/run-make-support/src/cc.rs @@ -0,0 +1,202 @@ +use std::env; +use std::path::Path; +use std::process::{Command, Output}; + +use crate::{bin_name, cygpath_windows, handle_failed_output, is_msvc, is_windows, tmp_dir, uname}; + +/// Construct a new platform-specific C compiler invocation. +/// +/// WARNING: This means that what flags are accepted by the underlying C compiler is +/// platform- AND compiler-specific. Consult the relevant docs for `gcc`, `clang` and `mvsc`. +pub fn cc() -> Cc { + Cc::new() +} + +/// A platform-specific C compiler invocation builder. The specific C compiler used is +/// passed down from compiletest. +#[derive(Debug)] +pub struct Cc { + cmd: Command, +} + +impl Cc { + /// Construct a new platform-specific C compiler invocation. + /// + /// WARNING: This means that what flags are accepted by the underlying C compile is + /// platform- AND compiler-specific. Consult the relevant docs for `gcc`, `clang` and `mvsc`. + pub fn new() -> Self { + let compiler = env::var("CC").unwrap(); + + let mut cmd = Command::new(compiler); + + let default_cflags = env::var("CC_DEFAULT_FLAGS").unwrap(); + for flag in default_cflags.split(char::is_whitespace) { + cmd.arg(flag); + } + + Self { cmd } + } + + /// Specify path of the input file. + pub fn input>(&mut self, path: P) -> &mut Self { + self.cmd.arg(path.as_ref()); + self + } + + /// Add a *platform-and-compiler-specific* argument. Please consult the docs for the various + /// possible C compilers on the various platforms to check which arguments are legal for + /// which compiler. + pub fn arg(&mut self, flag: &str) -> &mut Self { + self.cmd.arg(flag); + self + } + + /// Add multiple *platform-and-compiler-specific* arguments. Please consult the docs for the + /// various possible C compilers on the various platforms to check which arguments are legal + /// for which compiler. + pub fn args(&mut self, args: &[&str]) -> &mut Self { + self.cmd.args(args); + self + } + + /// Specify `-o` or `-Fe`/`-Fo` depending on platform/compiler. This assumes that the executable + /// is under `$TMPDIR`. + pub fn out_exe(&mut self, name: &str) -> &mut Self { + // Ref: tools.mk (irrelevant lines omitted): + // + // ```makefile + // ifdef IS_MSVC + // OUT_EXE=-Fe:`cygpath -w $(TMPDIR)/$(call BIN,$(1))` \ + // -Fo:`cygpath -w $(TMPDIR)/$(1).obj` + // else + // OUT_EXE=-o $(TMPDIR)/$(1) + // endif + // ``` + + if is_msvc() { + let fe_path = cygpath_windows(tmp_dir().join(bin_name(name))); + let fo_path = cygpath_windows(tmp_dir().join(format!("{name}.obj"))); + self.cmd.arg(format!("-Fe:{fe_path}")); + self.cmd.arg(format!("-Fo:{fo_path}")); + } else { + self.cmd.arg("-o"); + self.cmd.arg(tmp_dir().join(name)); + } + + self + } + + /// Run the constructed C invocation command and assert that it is successfully run. + #[track_caller] + pub fn run(&mut self) -> Output { + let caller_location = std::panic::Location::caller(); + let caller_line_number = caller_location.line(); + + let output = self.cmd.output().unwrap(); + if !output.status.success() { + handle_failed_output(&format!("{:#?}", self.cmd), output, caller_line_number); + } + output + } + + /// Inspect what the underlying [`Command`] is up to the current construction. + pub fn inspect(&mut self, f: impl FnOnce(&Command)) -> &mut Self { + f(&self.cmd); + self + } +} + +/// `EXTRACFLAGS` +pub fn extra_c_flags() -> Vec<&'static str> { + // Adapted from tools.mk (trimmed): + // + // ```makefile + // ifdef IS_WINDOWS + // ifdef IS_MSVC + // EXTRACFLAGS := ws2_32.lib userenv.lib advapi32.lib bcrypt.lib ntdll.lib synchronization.lib + // else + // EXTRACFLAGS := -lws2_32 -luserenv -lbcrypt -lntdll -lsynchronization + // endif + // else + // ifeq ($(UNAME),Darwin) + // EXTRACFLAGS := -lresolv + // else + // ifeq ($(UNAME),FreeBSD) + // EXTRACFLAGS := -lm -lpthread -lgcc_s + // else + // ifeq ($(UNAME),SunOS) + // EXTRACFLAGS := -lm -lpthread -lposix4 -lsocket -lresolv + // else + // ifeq ($(UNAME),OpenBSD) + // EXTRACFLAGS := -lm -lpthread -lc++abi + // else + // EXTRACFLAGS := -lm -lrt -ldl -lpthread + // endif + // endif + // endif + // endif + // endif + // ``` + + if is_windows() { + if is_msvc() { + vec![ + "ws2_32.lib", + "userenv.lib", + "advapi32.lib", + "bcrypt.lib", + "ntdll.lib", + "synchronization.lib", + ] + } else { + vec!["-lws2_32", "-luserenv", "-lbcrypt", "-lntdll", "-lsynchronization"] + } + } else { + match uname() { + n if n.contains("Darwin") => vec!["-lresolv"], + n if n.contains("FreeBSD") => vec!["-lm", "-lpthread", "-lgcc_s"], + n if n.contains("SunOS") => { + vec!["-lm", "-lpthread", "-lposix4", "-lsocket", "-lresolv"] + } + n if n.contains("OpenBSD") => vec!["-lm", "-lpthread", "-lc++abi"], + _ => vec!["-lm", "-lrt", "-ldl", "-lpthread"], + } + } +} + +/// `EXTRACXXFLAGS` +pub fn extra_cxx_flags() -> Vec<&'static str> { + // Adapted from tools.mk (trimmed): + // + // ```makefile + // ifdef IS_WINDOWS + // ifdef IS_MSVC + // else + // EXTRACXXFLAGS := -lstdc++ + // endif + // else + // ifeq ($(UNAME),Darwin) + // EXTRACXXFLAGS := -lc++ + // else + // ifeq ($(UNAME),FreeBSD) + // else + // ifeq ($(UNAME),SunOS) + // else + // ifeq ($(UNAME),OpenBSD) + // else + // EXTRACXXFLAGS := -lstdc++ + // endif + // endif + // endif + // endif + // endif + // ``` + if is_windows() { + if is_msvc() { vec![] } else { vec!["-lstdc++"] } + } else { + match uname() { + n if n.contains("Darwin") => vec!["-lc++"], + _ => vec!["-lstdc++"], + } + } +} diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index 7975677286d3c..e70acf5857165 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -1,14 +1,16 @@ +pub mod cc; pub mod run; pub mod rustc; pub mod rustdoc; use std::env; -use std::path::PathBuf; -use std::process::Output; +use std::path::{Path, PathBuf}; +use std::process::{Command, Output}; pub use object; pub use wasmparser; +pub use cc::{cc, extra_c_flags, extra_cxx_flags, Cc}; pub use run::{run, run_fail}; pub use rustc::{aux_build, rustc, Rustc}; pub use rustdoc::{bare_rustdoc, rustdoc, Rustdoc}; @@ -18,8 +20,95 @@ pub fn tmp_dir() -> PathBuf { env::var_os("TMPDIR").unwrap().into() } +/// `TARGET` +pub fn target() -> String { + env::var("TARGET").unwrap() +} + +/// Check if target is windows-like. +pub fn is_windows() -> bool { + env::var_os("IS_WINDOWS").is_some() +} + +/// Check if target uses msvc. +pub fn is_msvc() -> bool { + env::var_os("IS_MSVC").is_some() +} + +/// Construct a path to a static library under `$TMPDIR` given the library name. This will return a +/// path with `$TMPDIR` joined with platform-and-compiler-specific library name. +pub fn static_lib(name: &str) -> PathBuf { + tmp_dir().join(static_lib_name(name)) +} + +/// Construct the static library name based on the platform. +pub fn static_lib_name(name: &str) -> String { + // See tools.mk (irrelevant lines omitted): + // + // ```makefile + // ifeq ($(UNAME),Darwin) + // STATICLIB = $(TMPDIR)/lib$(1).a + // else + // ifdef IS_WINDOWS + // ifdef IS_MSVC + // STATICLIB = $(TMPDIR)/$(1).lib + // else + // STATICLIB = $(TMPDIR)/lib$(1).a + // endif + // else + // STATICLIB = $(TMPDIR)/lib$(1).a + // endif + // endif + // ``` + assert!(!name.contains(char::is_whitespace), "name cannot contain whitespace"); + + if target().contains("msvc") { format!("{name}.lib") } else { format!("lib{name}.a") } +} + +/// Construct the binary name based on platform. +pub fn bin_name(name: &str) -> String { + if is_windows() { format!("{name}.exe") } else { name.to_string() } +} + +/// Use `cygpath -w` on a path to get a Windows path string back. This assumes that `cygpath` is +/// available on the platform! +#[track_caller] +pub fn cygpath_windows>(path: P) -> String { + let caller_location = std::panic::Location::caller(); + let caller_line_number = caller_location.line(); + + let mut cygpath = Command::new("cygpath"); + cygpath.arg("-w"); + cygpath.arg(path.as_ref()); + let output = cygpath.output().unwrap(); + if !output.status.success() { + handle_failed_output(&format!("{:#?}", cygpath), output, caller_line_number); + } + let s = String::from_utf8(output.stdout).unwrap(); + // cygpath -w can attach a newline + s.trim().to_string() +} + +/// Run `uname`. This assumes that `uname` is available on the platform! +#[track_caller] +pub fn uname() -> String { + let caller_location = std::panic::Location::caller(); + let caller_line_number = caller_location.line(); + + let mut uname = Command::new("uname"); + let output = uname.output().unwrap(); + if !output.status.success() { + handle_failed_output(&format!("{:#?}", uname), output, caller_line_number); + } + String::from_utf8(output.stdout).unwrap() +} + fn handle_failed_output(cmd: &str, output: Output, caller_line_number: u32) -> ! { - eprintln!("command failed at line {caller_line_number}"); + if output.status.success() { + eprintln!("command incorrectly succeeded at line {caller_line_number}"); + } else { + eprintln!("command failed at line {caller_line_number}"); + } eprintln!("{cmd}"); eprintln!("output status: `{}`", output.status); eprintln!("=== STDOUT ===\n{}\n\n", String::from_utf8(output.stdout).unwrap()); diff --git a/src/tools/run-make-support/src/run.rs b/src/tools/run-make-support/src/run.rs index 42dcf54da22e0..e33ea9d6e40a3 100644 --- a/src/tools/run-make-support/src/run.rs +++ b/src/tools/run-make-support/src/run.rs @@ -2,17 +2,14 @@ use std::env; use std::path::{Path, PathBuf}; use std::process::{Command, Output}; -use super::handle_failed_output; +use crate::is_windows; -fn run_common(bin_name: &str) -> (Command, Output) { - let target = env::var("TARGET").unwrap(); - - let bin_name = - if target.contains("windows") { format!("{}.exe", bin_name) } else { bin_name.to_owned() }; +use super::{bin_name, handle_failed_output}; +fn run_common(name: &str) -> (Command, Output) { let mut bin_path = PathBuf::new(); bin_path.push(env::var("TMPDIR").unwrap()); - bin_path.push(&bin_name); + bin_path.push(&bin_name(name)); let ld_lib_path_envvar = env::var("LD_LIB_PATH_ENVVAR").unwrap(); let mut cmd = Command::new(bin_path); cmd.env(&ld_lib_path_envvar, { @@ -27,7 +24,7 @@ fn run_common(bin_name: &str) -> (Command, Output) { env::join_paths(paths.iter()).unwrap() }); - if target.contains("windows") { + if is_windows() { let mut paths = vec![]; for p in env::split_paths(&std::env::var("PATH").unwrap_or(String::new())) { paths.push(p.to_path_buf()); @@ -42,11 +39,11 @@ fn run_common(bin_name: &str) -> (Command, Output) { /// Run a built binary and make sure it succeeds. #[track_caller] -pub fn run(bin_name: &str) -> Output { +pub fn run(name: &str) -> Output { let caller_location = std::panic::Location::caller(); let caller_line_number = caller_location.line(); - let (cmd, output) = run_common(bin_name); + let (cmd, output) = run_common(name); if !output.status.success() { handle_failed_output(&format!("{:#?}", cmd), output, caller_line_number); } @@ -55,11 +52,11 @@ pub fn run(bin_name: &str) -> Output { /// Run a built binary and make sure it fails. #[track_caller] -pub fn run_fail(bin_name: &str) -> Output { +pub fn run_fail(name: &str) -> Output { let caller_location = std::panic::Location::caller(); let caller_line_number = caller_location.line(); - let (cmd, output) = run_common(bin_name); + let (cmd, output) = run_common(name); if output.status.success() { handle_failed_output(&format!("{:#?}", cmd), output, caller_line_number); } diff --git a/src/tools/run-make-support/src/rustc.rs b/src/tools/run-make-support/src/rustc.rs index 1b358817a7957..50ff0d26bbb8e 100644 --- a/src/tools/run-make-support/src/rustc.rs +++ b/src/tools/run-make-support/src/rustc.rs @@ -1,4 +1,5 @@ use std::env; +use std::ffi::OsStr; use std::path::Path; use std::process::{Command, Output}; @@ -133,6 +134,11 @@ impl Rustc { self } + pub fn env(&mut self, name: impl AsRef, value: impl AsRef) -> &mut Self { + self.cmd.env(name, value); + self + } + // Command inspection, output and running helper methods /// Get the [`Output`][std::process::Output] of the finished `rustc` process. @@ -153,6 +159,18 @@ impl Rustc { output } + #[track_caller] + pub fn run_fail(&mut self) -> Output { + let caller_location = std::panic::Location::caller(); + let caller_line_number = caller_location.line(); + + let output = self.cmd.output().unwrap(); + if output.status.success() { + handle_failed_output(&format!("{:#?}", self.cmd), output, caller_line_number); + } + output + } + /// Inspect what the underlying [`Command`] is up to the current construction. pub fn inspect(&mut self, f: impl FnOnce(&Command)) -> &mut Self { f(&self.cmd); diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml index 2d8946520d5e7..08ad10c29718c 100644 --- a/src/tools/rust-analyzer/.github/workflows/ci.yaml +++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml @@ -71,7 +71,7 @@ jobs: run: echo "::add-matcher::.github/rust.json" - name: Cache Dependencies - uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 + uses: Swatinem/rust-cache@640a22190e7a783d4c409684cea558f081f92012 with: key: ${{ env.RUST_CHANNEL }} @@ -140,7 +140,7 @@ jobs: rustup target add ${{ env.targets }} ${{ env.targets_ide }} - name: Cache Dependencies - uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 + uses: Swatinem/rust-cache@640a22190e7a783d4c409684cea558f081f92012 - name: Check run: | diff --git a/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml b/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml index 862373ec1cce0..34ca53e2e53ad 100644 --- a/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml +++ b/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml @@ -32,4 +32,5 @@ jobs: git config --global user.name "GitHub Action" # Remove r-a crates from the workspaces so we don't auto-publish them as well sed -i 's/ "crates\/\*"//' ./Cargo.toml + sed -i 's/ "xtask\/"//' ./Cargo.toml cargo workspaces publish --yes --exact --from-git --no-git-commit --allow-dirty diff --git a/src/tools/rust-analyzer/.github/workflows/release.yaml b/src/tools/rust-analyzer/.github/workflows/release.yaml index dc0a6c2d91f4b..11014338d72f5 100644 --- a/src/tools/rust-analyzer/.github/workflows/release.yaml +++ b/src/tools/rust-analyzer/.github/workflows/release.yaml @@ -36,6 +36,7 @@ jobs: - os: ubuntu-20.04 target: x86_64-unknown-linux-gnu code-target: linux-x64 + container: rockylinux:8 - os: ubuntu-20.04 target: aarch64-unknown-linux-gnu code-target: linux-arm64 @@ -58,10 +59,18 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: ${{ env.FETCH_DEPTH }} + - name: Install toolchain dependencies + if: matrix.container == 'rockylinux:8' + shell: bash + run: | + dnf install -y gcc + curl --proto '=https' --tlsv1.2 --retry 10 --retry-connrefused -fsSL "https://sh.rustup.rs" | sh -s -- --profile minimal --default-toolchain none -y + echo "${CARGO_HOME:-$HOME/.cargo}/bin" >> $GITHUB_PATH + - name: Install Rust toolchain run: | rustup update --no-self-update stable @@ -69,9 +78,9 @@ jobs: rustup component add rust-src - name: Install Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: - node-version: 16 + node-version: 18 - name: Update apt repositories if: matrix.target == 'aarch64-unknown-linux-gnu' || matrix.target == 'arm-unknown-linux-gnueabihf' @@ -181,7 +190,7 @@ jobs: - name: Install Nodejs uses: actions/setup-node@v4 with: - node-version: 18 + node-version: 20 - run: echo "TAG=$(date --iso -u)" >> $GITHUB_ENV if: github.ref == 'refs/heads/release' diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 68ed32391b7fe..c7cf4479b3301 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -594,6 +594,7 @@ dependencies = [ "rustc-hash", "scoped-tls", "smallvec", + "span", "stdx", "syntax", "test-fixture", @@ -637,6 +638,7 @@ dependencies = [ "pulldown-cmark", "pulldown-cmark-to-cmark", "smallvec", + "span", "stdx", "syntax", "test-fixture", @@ -732,6 +734,7 @@ dependencies = [ "ide-db", "itertools", "once_cell", + "paths", "serde_json", "stdx", "syntax", @@ -931,6 +934,7 @@ dependencies = [ "hir-expand", "ide-db", "itertools", + "paths", "proc-macro-api", "project-model", "span", @@ -1225,6 +1229,9 @@ checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "paths" version = "0.0.0" +dependencies = [ + "camino", +] [[package]] name = "percent-encoding" @@ -1375,6 +1382,7 @@ dependencies = [ "semver", "serde", "serde_json", + "span", "stdx", "toolchain", "tracing", @@ -1432,9 +1440,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_abi" -version = "0.42.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ae52e2d5b08762c9464b541345f519b8719d57b643b73632bade43ecece9dc" +checksum = "b8709df2a746f055316bc0c62bd30948695a25e734863bf6e1f9755403e010ab" dependencies = [ "bitflags 2.4.2", "ra-ap-rustc_index", @@ -1443,9 +1451,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index" -version = "0.42.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd7e10c7853fe79443d46e1d2d8ab09fe99926118e59653fb8b480d5045f126" +checksum = "9ad68bacffb87dcdbb23a3ce11261375078aaa06b85d348c49f39ffd5510dc20" dependencies = [ "arrayvec", "ra-ap-rustc_index_macros", @@ -1454,9 +1462,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index_macros" -version = "0.42.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47f1d1c589be6c9a9e852fadee0e60329c0f862e87442ac2fe5adae30663cc76" +checksum = "8782aaf3a113837c533dfb1c45df91cd17e1fdd1d2f9a20c2e0d1976025c4f1f" dependencies = [ "proc-macro2", "quote", @@ -1466,9 +1474,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.42.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa852373a757b4c723bbdc96ced7f575cad68a1e266e45fee12bc4c69a482d80" +checksum = "aab683fc8579d09eb72033bd5dc9ba6d701aa9645b5fed087ef19af71184dff3" dependencies = [ "unicode-properties", "unicode-xid", @@ -1476,9 +1484,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.42.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2afe3c49accd95a53ac4d72ae13bafc7d115bdd80c8cd56ab09e6fc68f482210" +checksum = "0bcf9ff5edbf784b67b8ad5e03a068f1300fcc24062c0d476b3018965135d933" dependencies = [ "ra-ap-rustc_index", "ra-ap-rustc_lexer", @@ -1486,9 +1494,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.42.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1253da23515d80c377a3998731e0ec3794997b62b989fd47db73efbde6a0bd7c" +checksum = "d63d1e1d5b2a13273cee1a10011147418f40e12b70f70578ce1dee0f1cafc334" dependencies = [ "ra-ap-rustc_index", "rustc-hash", @@ -1598,6 +1606,7 @@ dependencies = [ "oorandom", "parking_lot", "parser", + "paths", "proc-macro-api", "profile", "project-model", @@ -1869,20 +1878,16 @@ dependencies = [ "itertools", "once_cell", "parser", - "proc-macro2", - "quote", "ra-ap-rustc_lexer", "rayon", "rowan", "rustc-hash", "smol_str", - "sourcegen", "stdx", "test-utils", "text-edit", "tracing", "triomphe", - "ungrammar", ] [[package]] @@ -2024,6 +2029,7 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" name = "toolchain" version = "0.0.0" dependencies = [ + "camino", "home", ] @@ -2109,7 +2115,6 @@ name = "tt" version = "0.0.0" dependencies = [ "smol_str", - "span", "stdx", "text-size", ] @@ -2438,8 +2443,12 @@ version = "0.1.0" dependencies = [ "anyhow", "flate2", + "itertools", + "proc-macro2", + "quote", "stdx", "time", + "ungrammar", "write-json", "xflags", "xshell", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 0679522efd667..d9343d2b96325 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -84,11 +84,11 @@ tt = { path = "./crates/tt", version = "0.0.0" } vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.42.0", default-features = false } -ra-ap-rustc_parse_format = { version = "0.42.0", default-features = false } -ra-ap-rustc_index = { version = "0.42.0", default-features = false } -ra-ap-rustc_abi = { version = "0.42.0", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.42.0", default-features = false } +ra-ap-rustc_lexer = { version = "0.44.0", default-features = false } +ra-ap-rustc_parse_format = { version = "0.44.0", default-features = false } +ra-ap-rustc_index = { version = "0.44.0", default-features = false } +ra-ap-rustc_abi = { version = "0.44.0", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.44.0", default-features = false } # local crates that aren't published to crates.io. These should not have versions. sourcegen = { path = "./crates/sourcegen" } @@ -105,6 +105,7 @@ anyhow = "1.0.75" arrayvec = "0.7.4" bitflags = "2.4.1" cargo_metadata = "0.18.1" +camino = "1.1.6" chalk-solve = { version = "0.96.0", default-features = false } chalk-ir = "0.96.0" chalk-recursive = { version = "0.96.0", default-features = false } 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 b243b37b77b92..27eb05cd4dc56 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/input.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs @@ -6,11 +6,12 @@ //! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how //! actual IO is done and lowered to input. -use std::{fmt, mem, ops, str::FromStr}; +use std::{fmt, mem, ops}; use cfg::CfgOptions; use la_arena::{Arena, Idx, RawIdx}; use rustc_hash::{FxHashMap, FxHashSet}; +use span::Edition; use syntax::SmolStr; use triomphe::Arc; use vfs::{file_set::FileSet, AbsPathBuf, AnchoredPath, FileId, VfsPath}; @@ -293,42 +294,11 @@ pub struct CrateData { pub is_proc_macro: bool, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum Edition { - Edition2015, - Edition2018, - Edition2021, - Edition2024, -} - -impl Edition { - pub const CURRENT: Edition = Edition::Edition2021; - pub const DEFAULT: Edition = Edition::Edition2015; -} - #[derive(Default, Debug, Clone, PartialEq, Eq)] pub struct Env { entries: FxHashMap, } -impl Env { - pub fn new_for_test_fixture() -> Self { - Env { - entries: FxHashMap::from_iter([( - String::from("__ra_is_test_fixture"), - String::from("__ra_is_test_fixture"), - )]), - } - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum DependencyKind { - Normal, - Dev, - Build, -} - #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Dependency { pub crate_id: CrateId, @@ -530,13 +500,6 @@ impl CrateGraph { } } - // FIXME: this only finds one crate with the given root; we could have multiple - pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option { - let (crate_id, _) = - self.arena.iter().find(|(_crate_id, data)| data.root_file_id == file_id)?; - Some(crate_id) - } - pub fn sort_deps(&mut self) { self.arena .iter_mut() @@ -653,6 +616,10 @@ impl CrateGraph { } id_map } + + pub fn shrink_to_fit(&mut self) { + self.arena.shrink_to_fit(); + } } impl ops::Index for CrateGraph { @@ -670,32 +637,6 @@ impl CrateData { } } -impl FromStr for Edition { - type Err = ParseEditionError; - - fn from_str(s: &str) -> Result { - let res = match s { - "2015" => Edition::Edition2015, - "2018" => Edition::Edition2018, - "2021" => Edition::Edition2021, - "2024" => Edition::Edition2024, - _ => return Err(ParseEditionError { invalid_input: s.to_owned() }), - }; - Ok(res) - } -} - -impl fmt::Display for Edition { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(match self { - Edition::Edition2015 => "2015", - Edition::Edition2018 => "2018", - Edition::Edition2021 => "2021", - Edition::Edition2024 => "2024", - }) - } -} - impl Extend<(String, String)> for Env { fn extend>(&mut self, iter: T) { self.entries.extend(iter); @@ -722,19 +663,6 @@ impl Env { } } -#[derive(Debug)] -pub struct ParseEditionError { - invalid_input: String, -} - -impl fmt::Display for ParseEditionError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "invalid edition: {:?}", self.invalid_input) - } -} - -impl std::error::Error for ParseEditionError {} - #[derive(Debug)] pub struct CyclicDependenciesError { path: Vec<(CrateId, Option)>, diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs index 5dcb580723fa0..785ff9ceffada 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -14,9 +14,9 @@ use triomphe::Arc; pub use crate::{ change::FileChange, input::{ - CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, - DependencyKind, Edition, Env, LangCrateOrigin, ProcMacroPaths, ReleaseChannel, SourceRoot, - SourceRootId, TargetLayoutLoadResult, + CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Env, + LangCrateOrigin, ProcMacroPaths, ReleaseChannel, SourceRoot, SourceRootId, + TargetLayoutLoadResult, }, }; pub use salsa::{self, Cancelled}; diff --git a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs index f8efb52022205..4ee86954acd92 100644 --- a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs +++ b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs @@ -8,10 +8,10 @@ #![warn(rust_2018_idioms, unused_lifetimes)] -use std::{fmt, io, path::PathBuf, process::Command, time::Duration}; +use std::{fmt, io, process::Command, time::Duration}; use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; -use paths::{AbsPath, AbsPathBuf}; +use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::FxHashMap; use serde::Deserialize; @@ -53,7 +53,7 @@ pub enum FlycheckConfig { extra_args: Vec, extra_env: FxHashMap, ansi_color_output: bool, - target_dir: Option, + target_dir: Option, }, CustomCommand { command: String, @@ -363,7 +363,7 @@ impl FlycheckActor { }); cmd.arg("--manifest-path"); - cmd.arg(self.root.join("Cargo.toml").as_os_str()); + cmd.arg(self.root.join("Cargo.toml")); for target in target_triples { cmd.args(["--target", target.as_str()]); diff --git a/src/tools/rust-analyzer/crates/flycheck/src/test_runner.rs b/src/tools/rust-analyzer/crates/flycheck/src/test_runner.rs index 31378716b3ec4..9f761c9ead18a 100644 --- a/src/tools/rust-analyzer/crates/flycheck/src/test_runner.rs +++ b/src/tools/rust-analyzer/crates/flycheck/src/test_runner.rs @@ -55,13 +55,16 @@ pub struct CargoTestHandle { } // Example of a cargo test command: -// cargo test -- module::func -Z unstable-options --format=json +// cargo test --workspace --no-fail-fast -- module::func -Z unstable-options --format=json impl CargoTestHandle { pub fn new(path: Option<&str>) -> std::io::Result { let mut cmd = Command::new(Tool::Cargo.path()); cmd.env("RUSTC_BOOTSTRAP", "1"); cmd.arg("test"); + cmd.arg("--workspace"); + // --no-fail-fast is needed to ensure that all requested tests will run + cmd.arg("--no-fail-fast"); cmd.arg("--"); if let Some(path) = path { cmd.arg(path); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs index 21536098b82dd..fa7730f302ec7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs @@ -148,12 +148,12 @@ impl Attrs { } } - pub fn lang(&self) -> Option<&SmolStr> { + pub fn lang(&self) -> Option<&str> { self.by_key("lang").string_value() } pub fn lang_item(&self) -> Option { - self.by_key("lang").string_value().and_then(|it| LangItem::from_str(it)) + self.by_key("lang").string_value().and_then(LangItem::from_str) } pub fn has_doc_hidden(&self) -> bool { @@ -178,7 +178,7 @@ impl Attrs { self.doc_exprs().flat_map(|doc_expr| doc_expr.aliases().to_vec()) } - pub fn export_name(&self) -> Option<&SmolStr> { + pub fn export_name(&self) -> Option<&str> { self.by_key("export_name").string_value() } @@ -565,7 +565,7 @@ impl<'attr> AttrQuery<'attr> { self.attrs().filter_map(|attr| attr.token_tree_value()) } - pub fn string_value(self) -> Option<&'attr SmolStr> { + pub fn string_value(self) -> Option<&'attr str> { self.attrs().find_map(|attr| attr.string_value()) } 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 b815c9b73ef83..da790f11516d9 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs @@ -453,8 +453,8 @@ impl ProcMacroData { ( def.name, match def.kind { - ProcMacroKind::CustomDerive { helpers } => Some(helpers), - ProcMacroKind::FnLike | ProcMacroKind::Attr => None, + ProcMacroKind::Derive { helpers } => Some(helpers), + ProcMacroKind::Bang | ProcMacroKind::Attr => None, }, ) } else { @@ -484,10 +484,11 @@ impl ExternCrateDeclData { let extern_crate = &item_tree[loc.id.value]; let name = extern_crate.name.clone(); + let krate = loc.container.krate(); let crate_id = if name == hir_expand::name![self] { - Some(loc.container.krate()) + Some(krate) } else { - db.crate_def_map(loc.container.krate()) + db.crate_def_map(krate) .extern_prelude() .find(|&(prelude_name, ..)| *prelude_name == name) .map(|(_, (root, _))| root.krate()) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs index 1d2c7c3a55fd0..4638b3771973c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs @@ -22,8 +22,8 @@ use crate::{ lower::LowerCtx, nameres::{DefMap, MacroSubNs}, type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef}, - AdtId, ConstParamId, GenericDefId, HasModule, ItemTreeLoc, LocalTypeOrConstParamId, Lookup, - TypeOrConstParamId, TypeParamId, + AdtId, ConstParamId, GenericDefId, HasModule, ItemTreeLoc, LifetimeParamId, + LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId, }; /// Data about a generic type parameter (to a function, struct, impl, ...). @@ -102,6 +102,52 @@ impl TypeOrConstParamData { impl_from!(TypeParamData, ConstParamData for TypeOrConstParamData); +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub enum GenericParamData { + TypeParamData(TypeParamData), + ConstParamData(ConstParamData), + LifetimeParamData(LifetimeParamData), +} + +impl GenericParamData { + pub fn name(&self) -> Option<&Name> { + match self { + GenericParamData::TypeParamData(it) => it.name.as_ref(), + GenericParamData::ConstParamData(it) => Some(&it.name), + GenericParamData::LifetimeParamData(it) => Some(&it.name), + } + } + + pub fn type_param(&self) -> Option<&TypeParamData> { + match self { + GenericParamData::TypeParamData(it) => Some(it), + _ => None, + } + } + + pub fn const_param(&self) -> Option<&ConstParamData> { + match self { + GenericParamData::ConstParamData(it) => Some(it), + _ => None, + } + } + + pub fn lifetime_param(&self) -> Option<&LifetimeParamData> { + match self { + GenericParamData::LifetimeParamData(it) => Some(it), + _ => None, + } + } +} + +impl_from!(TypeParamData, ConstParamData, LifetimeParamData for GenericParamData); + +pub enum GenericParamDataRef<'a> { + TypeParamData(&'a TypeParamData), + ConstParamData(&'a ConstParamData), + LifetimeParamData(&'a LifetimeParamData), +} + /// Data about the generic parameters of a function, struct, impl, etc. #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct GenericParams { @@ -358,6 +404,15 @@ impl GenericParamsCollector { } impl GenericParams { + /// Number of Generic parameters (type_or_consts + lifetimes) + pub fn len(&self) -> usize { + self.type_or_consts.len() + self.lifetimes.len() + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + /// Iterator of type_or_consts field pub fn iter( &self, @@ -365,6 +420,13 @@ impl GenericParams { self.type_or_consts.iter() } + /// Iterator of lifetimes field + pub fn iter_lt( + &self, + ) -> impl DoubleEndedIterator, &LifetimeParamData)> { + self.lifetimes.iter() + } + pub(crate) fn generic_params_query( db: &dyn DefDatabase, def: GenericDefId, @@ -507,4 +569,18 @@ impl GenericParams { .then(|| id) }) } + + pub fn find_lifetime_by_name( + &self, + name: &Name, + parent: GenericDefId, + ) -> Option { + self.lifetimes.iter().find_map(|(id, p)| { + if &p.name == name { + Some(LifetimeParamId { local_id: id, parent }) + } else { + None + } + }) + } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs index 953bf6b85d64a..0c84057950bdf 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs @@ -526,7 +526,7 @@ impl Printer<'_> { } fn print_generic_params(&mut self, params: &GenericParams) { - if params.type_or_consts.is_empty() && params.lifetimes.is_empty() { + if params.is_empty() { return; } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index 7d98f6cfe88c1..3a07c678428e4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -192,7 +192,7 @@ impl LangItems { pub(crate) fn lang_attr(db: &dyn DefDatabase, item: AttrDefId) -> Option { let attrs = db.attrs(item); - attrs.by_key("lang").string_value().and_then(|it| LangItem::from_str(it)) + attrs.by_key("lang").string_value().and_then(LangItem::from_str) } pub(crate) fn notable_traits_in_deps( diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index 828842de7e8a8..46898ce542d6d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -73,7 +73,7 @@ use std::{ use base_db::{ impl_intern_key, salsa::{self, impl_intern_value_trivial}, - CrateId, Edition, + CrateId, }; use hir_expand::{ builtin_attr_macro::BuiltinAttrExpander, @@ -90,7 +90,7 @@ use hir_expand::{ use item_tree::ExternBlock; use la_arena::Idx; use nameres::DefMap; -use span::{AstIdNode, FileAstId, FileId, SyntaxContextId}; +use span::{AstIdNode, Edition, FileAstId, FileId, SyntaxContextId}; use stdx::impl_from; use syntax::{ast, AstNode}; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs index 965f329acb9eb..c5c26e26bc0e7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs @@ -1449,6 +1449,7 @@ ok!(); #[test] fn test_new_std_matches() { check( + //- edition:2021 r#" macro_rules! matches { ($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => { @@ -1480,6 +1481,90 @@ fn main() { ); } +#[test] +fn test_hygienic_pat() { + check( + r#" +//- /new.rs crate:new deps:old edition:2015 +old::make!(); +fn main() { + matches!(0, 0 | 1 if true); +} +//- /old.rs crate:old edition:2021 +#[macro_export] +macro_rules! make { + () => { + macro_rules! matches { + ($expression:expr, $pattern:pat if $guard:expr ) => { + match $expression { + $pattern if $guard => true, + _ => false + } + }; + } + } +} + "#, + expect![[r#" +macro_rules !matches { + ($expression: expr, $pattern: pat if $guard: expr) = > { + match $expression { + $pattern if $guard = > true , _ = > false + } + } + ; +} +fn main() { + match 0 { + 0|1 if true =>true , _=>false + }; +} +"#]], + ); + check( + r#" +//- /new.rs crate:new deps:old edition:2021 +old::make!(); +fn main() { + matches/*+errors*/!(0, 0 | 1 if true); +} +//- /old.rs crate:old edition:2015 +#[macro_export] +macro_rules! make { + () => { + macro_rules! matches { + ($expression:expr, $pattern:pat if $guard:expr ) => { + match $expression { + $pattern if $guard => true, + _ => false + } + }; + } + } +} + "#, + expect![[r#" +macro_rules !matches { + ($expression: expr, $pattern: pat if $guard: expr) = > { + match $expression { + $pattern if $guard = > true , _ = > false + } + } + ; +} +fn main() { + /* error: unexpected token in input *//* parse error: expected expression */ +/* parse error: expected FAT_ARROW */ +/* parse error: expected `,` */ +/* parse error: expected pattern */ +match 0 { + 0 if $guard=>true , _=>false + }; +} +"#]], + ); +} + #[test] fn test_dollar_crate_lhs_is_not_meta() { check( 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 b56dee3efb554..a528c4cc6972d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -59,14 +59,14 @@ mod tests; use std::ops::Deref; -use base_db::{CrateId, Edition, FileId}; +use base_db::{CrateId, FileId}; use hir_expand::{ name::Name, proc_macro::ProcMacroKind, ErasedAstId, HirFileId, InFile, MacroCallId, MacroDefId, }; use itertools::Itertools; use la_arena::Arena; use rustc_hash::{FxHashMap, FxHashSet}; -use span::{FileAstId, ROOT_ERASED_FILE_AST_ID}; +use span::{Edition, FileAstId, ROOT_ERASED_FILE_AST_ID}; use stdx::format_to; use syntax::{ast, SmolStr}; use triomphe::Arc; @@ -737,7 +737,7 @@ impl MacroSubNs { MacroId::ProcMacroId(it) => { return match it.lookup(db).kind { ProcMacroKind::CustomDerive | ProcMacroKind::Attr => Self::Attr, - ProcMacroKind::FuncLike => Self::Bang, + ProcMacroKind::Bang => Self::Bang, }; } }; 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 662c80edf3247..eb7f4c05ae217 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 @@ -136,6 +136,7 @@ pub(super) fn derive_macro_as_call_id( call_site: SyntaxContextId, krate: CrateId, resolver: impl Fn(path::ModPath) -> Option<(MacroId, MacroDefId)>, + derive_macro_id: MacroCallId, ) -> Result<(MacroId, MacroDefId, MacroCallId), UnresolvedMacro> { let (macro_id, def_id) = resolver(item_attr.path.clone()) .filter(|(_, def_id)| def_id.is_derive()) @@ -147,6 +148,7 @@ pub(super) fn derive_macro_as_call_id( ast_id: item_attr.ast_id, derive_index: derive_pos, derive_attr_index, + derive_macro_id, }, call_site, ); 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 3d026447fb743..ae8f028e488dc 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 @@ -5,7 +5,7 @@ use std::{cmp::Ordering, iter, mem, ops::Not}; -use base_db::{CrateId, Dependency, Edition, FileId}; +use base_db::{CrateId, Dependency, FileId}; use cfg::{CfgExpr, CfgOptions}; use either::Either; use hir_expand::{ @@ -22,9 +22,9 @@ use itertools::{izip, Itertools}; use la_arena::Idx; use limit::Limit; use rustc_hash::{FxHashMap, FxHashSet}; -use span::{ErasedFileAstId, FileAstId, Span, SyntaxContextId}; +use span::{Edition, ErasedFileAstId, FileAstId, Span, SyntaxContextId}; use stdx::always; -use syntax::{ast, SmolStr}; +use syntax::ast; use triomphe::Arc; use crate::{ @@ -237,6 +237,8 @@ enum MacroDirectiveKind { derive_attr: AttrId, derive_pos: usize, ctxt: SyntaxContextId, + /// The "parent" macro it is resolved to. + derive_macro_id: MacroCallId, }, Attr { ast_id: AstIdWithPath, @@ -312,7 +314,7 @@ impl DefCollector<'_> { } } () if *attr_name == hir_expand::name![crate_type] => { - if let Some("proc-macro") = attr.string_value().map(SmolStr::as_str) { + if let Some("proc-macro") = attr.string_value() { self.is_proc_macro = true; } } @@ -602,7 +604,7 @@ impl DefCollector<'_> { .intern(self.db); self.define_proc_macro(def.name.clone(), proc_macro_id); let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap(); - if let ProcMacroKind::CustomDerive { helpers } = def.kind { + if let ProcMacroKind::Derive { helpers } = def.kind { crate_data.exported_derives.insert(self.db.macro_def(proc_macro_id.into()), helpers); } crate_data.fn_proc_macro_mapping.insert(fn_id, proc_macro_id); @@ -1146,7 +1148,13 @@ impl DefCollector<'_> { return Resolved::Yes; } } - MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos, ctxt: call_site } => { + MacroDirectiveKind::Derive { + ast_id, + derive_attr, + derive_pos, + ctxt: call_site, + derive_macro_id, + } => { let id = derive_macro_as_call_id( self.db, ast_id, @@ -1155,6 +1163,7 @@ impl DefCollector<'_> { *call_site, self.def_map.krate, resolver, + *derive_macro_id, ); if let Ok((macro_id, def_id, call_id)) = id { @@ -1224,6 +1233,8 @@ impl DefCollector<'_> { _ => return Resolved::No, }; + let call_id = + attr_macro_as_call_id(self.db, file_ast_id, attr, self.def_map.krate, def); if let MacroDefId { kind: MacroDefKind::BuiltInAttr( @@ -1252,6 +1263,7 @@ impl DefCollector<'_> { return recollect_without(self); } }; + let ast_id = ast_id.with_value(ast_adt_id); match attr.parse_path_comma_token_tree(self.db.upcast()) { @@ -1267,6 +1279,7 @@ impl DefCollector<'_> { derive_attr: attr.id, derive_pos: idx, ctxt: call_site.ctx, + derive_macro_id: call_id, }, container: directive.container, }); @@ -1301,10 +1314,6 @@ impl DefCollector<'_> { return recollect_without(self); } - // Not resolved to a derive helper or the derive attribute, so try to treat as a normal attribute. - let call_id = - attr_macro_as_call_id(self.db, file_ast_id, attr, self.def_map.krate, def); - // Skip #[test]/#[bench] expansion, which would merely result in more memory usage // due to duplicating functions into macro expansions if matches!( @@ -1460,13 +1469,20 @@ impl DefCollector<'_> { )); } } - MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos, ctxt: _ } => { + MacroDirectiveKind::Derive { + ast_id, + derive_attr, + derive_pos, + derive_macro_id, + .. + } => { self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call( directive.module_id, MacroCallKind::Derive { ast_id: ast_id.ast_id, derive_attr_index: *derive_attr, derive_index: *derive_pos as u32, + derive_macro_id: *derive_macro_id, }, ast_id.path.clone(), )); @@ -1902,7 +1918,7 @@ impl ModCollector<'_, '_> { } fn collect_module(&mut self, module_id: FileItemTreeId, attrs: &Attrs) { - let path_attr = attrs.by_key("path").string_value().map(SmolStr::as_str); + let path_attr = attrs.by_key("path").string_value(); let is_macro_use = attrs.by_key("macro_use").exists(); let module = &self.item_tree[module_id]; match &module.kind { @@ -2146,7 +2162,7 @@ impl ModCollector<'_, '_> { Some(it) => { // FIXME: a hacky way to create a Name from string. name = tt::Ident { - text: it.clone(), + text: it.into(), span: Span { range: syntax::TextRange::empty(syntax::TextSize::new(0)), anchor: span::SpanAnchor { 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 9e53b03728304..ee29b89f3d3b5 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 @@ -10,8 +10,8 @@ //! //! `ReachedFixedPoint` signals about this. -use base_db::Edition; use hir_expand::{name::Name, Lookup}; +use span::Edition; use triomphe::Arc; use crate::{ 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 c126fdac1c62f..5052708dc9391 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 @@ -13,18 +13,16 @@ pub struct ProcMacroDef { #[derive(Debug, PartialEq, Eq)] pub enum ProcMacroKind { - CustomDerive { helpers: Box<[Name]> }, - FnLike, + Derive { helpers: Box<[Name]> }, + Bang, Attr, } impl ProcMacroKind { pub(super) fn to_basedb_kind(&self) -> hir_expand::proc_macro::ProcMacroKind { match self { - ProcMacroKind::CustomDerive { .. } => { - hir_expand::proc_macro::ProcMacroKind::CustomDerive - } - ProcMacroKind::FnLike => hir_expand::proc_macro::ProcMacroKind::FuncLike, + ProcMacroKind::Derive { .. } => hir_expand::proc_macro::ProcMacroKind::CustomDerive, + ProcMacroKind::Bang => hir_expand::proc_macro::ProcMacroKind::Bang, ProcMacroKind::Attr => hir_expand::proc_macro::ProcMacroKind::Attr, } } @@ -34,13 +32,13 @@ impl Attrs { #[rustfmt::skip] pub fn parse_proc_macro_decl(&self, func_name: &Name) -> Option { if self.is_proc_macro() { - Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::FnLike }) + Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::Bang }) } else if self.is_proc_macro_attribute() { Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::Attr }) } else if self.by_key("proc_macro_derive").exists() { let derive = self.by_key("proc_macro_derive").tt_values().next()?; let def = parse_macro_name_and_helper_attrs(&derive.token_trees) - .map(|(name, helpers)| ProcMacroDef { name, kind: ProcMacroKind::CustomDerive { helpers } }); + .map(|(name, helpers)| ProcMacroDef { name, kind: ProcMacroKind::Derive { helpers } }); if def.is_none() { tracing::trace!("malformed `#[proc_macro_derive]`: {}", derive); 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 226d6f513f5d6..fadab858aa149 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -24,6 +24,7 @@ use crate::{ nameres::{DefMap, MacroSubNs}, path::{ModPath, Path, PathKind}, per_ns::PerNs, + type_ref::LifetimeRef, visibility::{RawVisibility, Visibility}, AdtId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, @@ -120,6 +121,12 @@ pub enum ValueNs { GenericParam(ConstParamId), } +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum LifetimeNs { + Static, + LifetimeParam(LifetimeParamId), +} + impl Resolver { /// Resolve known trait from std, like `std::futures::Future` pub fn resolve_known_trait(&self, db: &dyn DefDatabase, path: &ModPath) -> Option { @@ -418,6 +425,19 @@ impl Resolver { self.resolve_path_as_macro(db, path, expected_macro_kind).map(|(it, _)| db.macro_def(it)) } + pub fn resolve_lifetime(&self, lifetime: &LifetimeRef) -> Option { + if lifetime.name == name::known::STATIC_LIFETIME { + return Some(LifetimeNs::Static); + } + + self.scopes().find_map(|scope| match scope { + Scope::GenericParams { def, params } => { + params.find_lifetime_by_name(&lifetime.name, *def).map(LifetimeNs::LifetimeParam) + } + _ => None, + }) + } + /// Returns a set of names available in the current scope. /// /// Note that this is a somewhat fuzzy concept -- internally, the compiler 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 af3ecdcd5e32a..f1540498f2664 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs @@ -8,8 +8,8 @@ use intern::Interned; use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct}; use smallvec::{smallvec, SmallVec}; use span::{Span, SyntaxContextId}; -use syntax::{ast, match_ast, AstNode, AstToken, SmolStr, SyntaxNode}; -use triomphe::Arc; +use syntax::{ast, format_smolstr, match_ast, AstNode, AstToken, SmolStr, SyntaxNode}; +use triomphe::ThinArc; use crate::{ db::ExpandDatabase, @@ -22,8 +22,7 @@ use crate::{ /// Syntactical attributes, without filtering of `cfg_attr`s. #[derive(Default, Debug, Clone, PartialEq, Eq)] pub struct RawAttrs { - // FIXME: Make this a ThinArc - entries: Option>, + entries: Option>, } impl ops::Deref for RawAttrs { @@ -31,7 +30,7 @@ impl ops::Deref for RawAttrs { fn deref(&self) -> &[Attr] { match &self.entries { - Some(it) => it, + Some(it) => &it.slice, None => &[], } } @@ -45,20 +44,34 @@ impl RawAttrs { owner: &dyn ast::HasAttrs, span_map: SpanMapRef<'_>, ) -> Self { - let entries = collect_attrs(owner).filter_map(|(id, attr)| match attr { - Either::Left(attr) => { - attr.meta().and_then(|meta| Attr::from_src(db, meta, span_map, id)) - } - Either::Right(comment) => comment.doc_comment().map(|doc| Attr { - id, - input: Some(Interned::new(AttrInput::Literal(SmolStr::new(doc)))), - path: Interned::new(ModPath::from(crate::name!(doc))), - ctxt: span_map.span_for_range(comment.syntax().text_range()).ctx, - }), - }); - let entries: Arc<[Attr]> = Arc::from_iter(entries); + let entries: Vec<_> = collect_attrs(owner) + .filter_map(|(id, attr)| match attr { + Either::Left(attr) => { + attr.meta().and_then(|meta| Attr::from_src(db, meta, span_map, id)) + } + Either::Right(comment) => comment.doc_comment().map(|doc| { + let span = span_map.span_for_range(comment.syntax().text_range()); + Attr { + id, + input: Some(Interned::new(AttrInput::Literal(tt::Literal { + // FIXME: Escape quotes from comment content + text: SmolStr::new(format_smolstr!("\"{doc}\"",)), + span, + }))), + path: Interned::new(ModPath::from(crate::name!(doc))), + ctxt: span.ctx, + } + }), + }) + .collect(); - Self { entries: if entries.is_empty() { None } else { Some(entries) } } + let entries = if entries.is_empty() { + None + } else { + Some(ThinArc::from_header_and_iter((), entries.into_iter())) + }; + + RawAttrs { entries } } pub fn from_attrs_owner( @@ -75,16 +88,20 @@ impl RawAttrs { (None, entries @ Some(_)) => Self { entries }, (Some(entries), None) => Self { entries: Some(entries.clone()) }, (Some(a), Some(b)) => { - let last_ast_index = a.last().map_or(0, |it| it.id.ast_index() + 1) as u32; - Self { - entries: Some(Arc::from_iter(a.iter().cloned().chain(b.iter().map(|it| { + let last_ast_index = a.slice.last().map_or(0, |it| it.id.ast_index() + 1) as u32; + let items = a + .slice + .iter() + .cloned() + .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 - })))), - } + })) + .collect::>(); + Self { entries: Some(ThinArc::from_header_and_iter((), items.into_iter())) } } } } @@ -100,41 +117,47 @@ impl RawAttrs { } let crate_graph = db.crate_graph(); - let new_attrs = Arc::from_iter(self.iter().flat_map(|attr| -> SmallVec<[_; 1]> { - let is_cfg_attr = - attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]); - if !is_cfg_attr { - return smallvec![attr.clone()]; - } - - let subtree = match attr.token_tree_value() { - Some(it) => it, - _ => return smallvec![attr.clone()], - }; - - let (cfg, parts) = match parse_cfg_attr_input(subtree) { - Some(it) => it, - None => return smallvec![attr.clone()], - }; - let index = attr.id; - let attrs = parts - .enumerate() - .take(1 << AttrId::CFG_ATTR_BITS) - .filter_map(|(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx))); - - let cfg_options = &crate_graph[krate].cfg_options; - let cfg = Subtree { delimiter: subtree.delimiter, token_trees: Box::from(cfg) }; - let cfg = CfgExpr::parse(&cfg); - if cfg_options.check(&cfg) == Some(false) { - smallvec![] - } else { - cov_mark::hit!(cfg_attr_active); - - attrs.collect() - } - })); + let new_attrs = + self.iter() + .flat_map(|attr| -> SmallVec<[_; 1]> { + let is_cfg_attr = + attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]); + if !is_cfg_attr { + return smallvec![attr.clone()]; + } - RawAttrs { entries: Some(new_attrs) } + let subtree = match attr.token_tree_value() { + Some(it) => it, + _ => return smallvec![attr.clone()], + }; + + let (cfg, parts) = match parse_cfg_attr_input(subtree) { + Some(it) => it, + None => return smallvec![attr.clone()], + }; + let index = attr.id; + let attrs = parts.enumerate().take(1 << AttrId::CFG_ATTR_BITS).filter_map( + |(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx)), + ); + + let cfg_options = &crate_graph[krate].cfg_options; + let cfg = Subtree { delimiter: subtree.delimiter, token_trees: Box::from(cfg) }; + let cfg = CfgExpr::parse(&cfg); + if cfg_options.check(&cfg) == Some(false) { + smallvec![] + } else { + cov_mark::hit!(cfg_attr_active); + + attrs.collect() + } + }) + .collect::>(); + let entries = if new_attrs.is_empty() { + None + } else { + Some(ThinArc::from_header_and_iter((), new_attrs.into_iter())) + }; + RawAttrs { entries } } } @@ -179,8 +202,7 @@ pub struct Attr { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum AttrInput { /// `#[attr = "string"]` - // FIXME: This is losing span - Literal(SmolStr), + Literal(tt::Literal), /// `#[attr(subtree)]` TokenTree(Box), } @@ -188,7 +210,7 @@ pub enum AttrInput { impl fmt::Display for AttrInput { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - AttrInput::Literal(lit) => write!(f, " = \"{}\"", lit.escape_debug()), + AttrInput::Literal(lit) => write!(f, " = {lit}"), AttrInput::TokenTree(tt) => tt.fmt(f), } } @@ -208,11 +230,10 @@ impl Attr { })?); let span = span_map.span_for_range(range); let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() { - let value = match lit.kind() { - ast::LiteralKind::String(string) => string.value()?.into(), - _ => lit.syntax().first_token()?.text().trim_matches('"').into(), - }; - Some(Interned::new(AttrInput::Literal(value))) + Some(Interned::new(AttrInput::Literal(tt::Literal { + text: lit.token().text().into(), + span, + }))) } else if let Some(tt) = ast.token_tree() { let tree = syntax_node_to_token_tree(tt.syntax(), span_map, span); Some(Interned::new(AttrInput::TokenTree(Box::new(tree)))) @@ -245,9 +266,8 @@ impl Attr { } Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '=', .. }))) => { let input = match input.get(1) { - Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { text, .. }))) => { - //FIXME the trimming here isn't quite right, raw strings are not handled - Some(Interned::new(AttrInput::Literal(text.trim_matches('"').into()))) + Some(tt::TokenTree::Leaf(tt::Leaf::Literal(lit))) => { + Some(Interned::new(AttrInput::Literal(lit.clone()))) } _ => None, }; @@ -265,9 +285,14 @@ impl Attr { impl Attr { /// #[path = "string"] - pub fn string_value(&self) -> Option<&SmolStr> { + pub fn string_value(&self) -> Option<&str> { match self.input.as_deref()? { - AttrInput::Literal(it) => Some(it), + AttrInput::Literal(it) => match it.text.strip_prefix('r') { + Some(it) => it.trim_matches('#'), + None => it.text.as_str(), + } + .strip_prefix('"')? + .strip_suffix('"'), _ => None, } } 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 9fb6a0b2346ba..fd3e4e7a4dba4 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 @@ -1,11 +1,11 @@ //! Builtin macro -use base_db::{AnchoredPath, Edition, FileId}; +use base_db::{AnchoredPath, FileId}; use cfg::CfgExpr; use either::Either; use itertools::Itertools; use mbe::{parse_exprs_with_sep, parse_to_token_tree}; -use span::{Span, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID}; +use span::{Edition, Span, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID}; use syntax::ast::{self, AstToken}; use crate::{ diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs index c74c13a6fd3c5..db3558a84e9e3 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs @@ -10,7 +10,7 @@ use syntax::{ use tracing::{debug, warn}; use tt::SmolStr; -use crate::{db::ExpandDatabase, MacroCallKind, MacroCallLoc}; +use crate::{db::ExpandDatabase, proc_macro::ProcMacroKind, MacroCallLoc, MacroDefKind}; fn check_cfg_attr(attr: &Attr, loc: &MacroCallLoc, db: &dyn ExpandDatabase) -> Option { if !attr.simple_name().as_deref().map(|v| v == "cfg")? { @@ -139,7 +139,7 @@ fn process_enum( 'variant: for variant in variants.variants() { for attr in variant.attrs() { if check_cfg_attr(&attr, loc, db).map(|enabled| !enabled).unwrap_or_default() { - // Rustc does not strip the attribute if it is enabled. So we will will leave it + // Rustc does not strip the attribute if it is enabled. So we will leave it debug!("censoring type {:?}", variant.syntax()); remove.insert(variant.syntax().clone().into()); // We need to remove the , as well @@ -180,7 +180,13 @@ pub(crate) fn process_cfg_attrs( db: &dyn ExpandDatabase, ) -> Option> { // FIXME: #[cfg_eval] is not implemented. But it is not stable yet - if !matches!(loc.kind, MacroCallKind::Derive { .. }) { + let is_derive = match loc.def.kind { + MacroDefKind::BuiltInDerive(..) + | MacroDefKind::ProcMacro(_, ProcMacroKind::CustomDerive, _) => true, + MacroDefKind::BuiltInAttr(expander, _) => expander.is_derive(), + _ => false, + }; + if !is_derive { return None; } let mut remove = FxHashSet::default(); 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 ec68f2f96e5e1..5461c1c49a32b 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -24,7 +24,8 @@ use crate::{ HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind, MacroCallLoc, 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); /// 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 @@ -98,7 +99,13 @@ pub trait ExpandDatabase: SourceDatabase { /// Lowers syntactic macro call to a token tree representation. That's a firewall /// query, only typing in the macro call itself changes the returned /// subtree. - fn macro_arg(&self, id: MacroCallId) -> (Arc, SyntaxFixupUndoInfo, Span); + fn macro_arg(&self, id: MacroCallId) -> MacroArgResult; + #[salsa::transparent] + fn macro_arg_considering_derives( + &self, + id: MacroCallId, + kind: &MacroCallKind, + ) -> MacroArgResult; /// Fetches the expander for this macro. #[salsa::transparent] #[salsa::invoke(TokenExpander::macro_expander)] @@ -144,7 +151,7 @@ pub fn expand_speculative( let span_map = RealSpanMap::absolute(FileId::BOGUS); let span_map = SpanMapRef::RealSpanMap(&span_map); - let (_, _, span) = db.macro_arg(actual_macro_call); + let (_, _, span) = db.macro_arg_considering_derives(actual_macro_call, &loc.kind); // Build the subtree and token mapping for the speculative args let (mut tt, undo_info) = match loc.kind { @@ -339,12 +346,24 @@ pub(crate) fn parse_with_map( } } -// FIXME: for derive attributes, this will return separate copies of the same structures! Though -// they may differ in spans due to differing call sites... -fn macro_arg( +/// This resolves the [MacroCallId] to check if it is a derive macro if so get the [macro_arg] for the derive. +/// Other wise return the [macro_arg] for the macro_call_id. +/// +/// This is not connected to the database so it does not cached the result. However, the inner [macro_arg] query is +fn macro_arg_considering_derives( db: &dyn ExpandDatabase, id: MacroCallId, -) -> (Arc, SyntaxFixupUndoInfo, Span) { + kind: &MacroCallKind, +) -> MacroArgResult { + match kind { + // Get the macro arg for the derive macro + MacroCallKind::Derive { derive_macro_id, .. } => db.macro_arg(*derive_macro_id), + // Normal macro arg + _ => db.macro_arg(id), + } +} + +fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { let loc = db.lookup_intern_macro_call(id); if let MacroCallLoc { @@ -414,29 +433,30 @@ fn macro_arg( } return (Arc::new(tt), SyntaxFixupUndoInfo::NONE, span); } - MacroCallKind::Derive { ast_id, derive_attr_index, .. } => { - let node = ast_id.to_ptr(db).to_node(&root); - let censor_derive_input = censor_derive_input(derive_attr_index, &node); - let item_node = node.into(); - let attr_source = attr_source(derive_attr_index, &item_node); - // FIXME: This is wrong, this should point to the path of the derive attribute` - let span = - map.span_for_range(attr_source.as_ref().and_then(|it| it.path()).map_or_else( - || item_node.syntax().text_range(), - |it| it.syntax().text_range(), - )); - (censor_derive_input, item_node, span) + // MacroCallKind::Derive should not be here. As we are getting the argument for the derive macro + MacroCallKind::Derive { .. } => { + unreachable!("`ExpandDatabase::macro_arg` called with `MacroCallKind::Derive`") } MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => { let node = ast_id.to_ptr(db).to_node(&root); let attr_source = attr_source(invoc_attr_index, &node); + let span = map.span_for_range( attr_source .as_ref() .and_then(|it| it.path()) .map_or_else(|| node.syntax().text_range(), |it| it.syntax().text_range()), ); - (attr_source.into_iter().map(|it| it.syntax().clone().into()).collect(), node, span) + // If derive attribute we need to censor the derive input + if matches!(loc.def.kind, MacroDefKind::BuiltInAttr(expander, ..) if expander.is_derive()) + && ast::Adt::can_cast(node.syntax().kind()) + { + let adt = ast::Adt::cast(node.syntax().clone()).unwrap(); + let censor_derive_input = censor_derive_input(invoc_attr_index, &adt); + (censor_derive_input, node, span) + } else { + (attr_source.into_iter().map(|it| it.syntax().clone().into()).collect(), node, span) + } } }; @@ -526,7 +546,8 @@ fn macro_expand( let (ExpandResult { value: tt, err }, span) = match loc.def.kind { MacroDefKind::ProcMacro(..) => return db.expand_proc_macro(macro_call_id).map(CowArc::Arc), _ => { - let (macro_arg, undo_info, span) = db.macro_arg(macro_call_id); + let (macro_arg, undo_info, span) = + db.macro_arg_considering_derives(macro_call_id, &loc.kind); let arg = &*macro_arg; let res = @@ -603,7 +624,7 @@ fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId) -> Span { 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(id); + let (macro_arg, undo_info, span) = db.macro_arg_considering_derives(id, &loc.kind); let (expander, ast) = match loc.def.kind { MacroDefKind::ProcMacro(expander, _, ast) => (expander, ast), 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 33643c02724f7..9a0b218e6d1d2 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs @@ -1,8 +1,8 @@ //! Compiled declarative macro expanders (`macro_rules!`` and `macro`) use std::sync::OnceLock; -use base_db::{CrateId, Edition, VersionReq}; -use span::{MacroCallId, Span}; +use base_db::{CrateId, VersionReq}; +use span::{MacroCallId, Span, SyntaxContextId}; use syntax::{ast, AstNode}; use triomphe::Arc; @@ -10,13 +10,13 @@ use crate::{ attrs::RawAttrs, db::ExpandDatabase, hygiene::{apply_mark, Transparency}, - tt, AstId, ExpandError, ExpandResult, + tt, AstId, ExpandError, ExpandResult, Lookup, }; /// Old-style `macro_rules` or the new macros 2.0 #[derive(Debug, Clone, Eq, PartialEq)] pub struct DeclarativeMacroExpander { - pub mac: mbe::DeclarativeMacro, + pub mac: mbe::DeclarativeMacro, pub transparency: Transparency, } @@ -94,8 +94,6 @@ impl DeclarativeMacroExpander { def_crate: CrateId, id: AstId, ) -> Arc { - let crate_data = &db.crate_graph()[def_crate]; - let is_2021 = crate_data.edition >= Edition::Edition2021; let (root, map) = crate::db::parse_with_map(db, id.file_id); let root = root.syntax_node(); @@ -133,6 +131,16 @@ impl DeclarativeMacroExpander { ) }); + let edition = |ctx: SyntaxContextId| { + let crate_graph = db.crate_graph(); + if ctx.is_root() { + crate_graph[def_crate].edition + } else { + let data = db.lookup_intern_syntax_context(ctx); + // UNWRAP-SAFETY: Only the root context has no outer expansion + crate_graph[data.outer_expn.unwrap().lookup(db).def.krate].edition + } + }; let (mac, transparency) = match id.to_ptr(db).to_node(&root) { ast::Macro::MacroRules(macro_rules) => ( match macro_rules.token_tree() { @@ -145,12 +153,11 @@ impl DeclarativeMacroExpander { ), ); - mbe::DeclarativeMacro::parse_macro_rules(&tt, is_2021, new_meta_vars) + mbe::DeclarativeMacro::parse_macro_rules(&tt, edition, new_meta_vars) } - None => mbe::DeclarativeMacro::from_err( - mbe::ParseError::Expected("expected a token tree".into()), - is_2021, - ), + None => mbe::DeclarativeMacro::from_err(mbe::ParseError::Expected( + "expected a token tree".into(), + )), }, transparency(¯o_rules).unwrap_or(Transparency::SemiTransparent), ), @@ -163,12 +170,11 @@ impl DeclarativeMacroExpander { map.span_for_range(macro_def.macro_token().unwrap().text_range()), ); - mbe::DeclarativeMacro::parse_macro2(&tt, is_2021, new_meta_vars) + mbe::DeclarativeMacro::parse_macro2(&tt, edition, new_meta_vars) } - None => mbe::DeclarativeMacro::from_err( - mbe::ParseError::Expected("expected a token tree".into()), - is_2021, - ), + None => mbe::DeclarativeMacro::from_err(mbe::ParseError::Expected( + "expected a token tree".into(), + )), }, transparency(¯o_def).unwrap_or(Transparency::Opaque), ), 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 5d4f7dc1462a8..db8bbeccef8f9 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -30,10 +30,11 @@ use triomphe::Arc; use std::{fmt, hash::Hash}; -use base_db::{salsa::impl_intern_value_trivial, CrateId, Edition, FileId}; +use base_db::{salsa::impl_intern_value_trivial, CrateId, FileId}; use either::Either; use span::{ - ErasedFileAstId, FileRange, HirFileIdRepr, Span, SpanAnchor, SyntaxContextData, SyntaxContextId, + Edition, ErasedFileAstId, FileRange, HirFileIdRepr, Span, SpanAnchor, SyntaxContextData, + SyntaxContextId, }; use syntax::{ ast::{self, AstNode}, @@ -53,11 +54,9 @@ use crate::{ pub use crate::files::{AstId, ErasedAstId, InFile, InMacroFile, InRealFile}; -pub use mbe::ValueResult; +pub use mbe::{DeclarativeMacro, ValueResult}; pub use span::{HirFileId, MacroCallId, MacroFileId}; -pub type DeclarativeMacro = ::mbe::DeclarativeMacro; - pub mod tt { pub use span::Span; pub use tt::{DelimiterKind, Spacing}; @@ -201,7 +200,7 @@ pub struct EagerCallInfo { /// Call id of the eager macro's input file (this is the macro file for its fully expanded input). arg_id: MacroCallId, error: Option, - /// TODO: Doc + /// The call site span of the eager macro span: Span, } @@ -212,7 +211,7 @@ pub enum MacroCallKind { expand_to: ExpandTo, /// Some if this is a macro call for an eager macro. Note that this is `None` /// for the eager input macro file. - // FIXME: This is being interned, subtrees can vary quickly differ just slightly causing + // FIXME: This is being interned, subtrees can vary quickly differing just slightly causing // leakage problems here eager: Option>, }, @@ -225,6 +224,9 @@ pub enum MacroCallKind { derive_attr_index: AttrId, /// Index of the derive macro in the derive attribute derive_index: u32, + /// The "parent" macro call. + /// We will resolve the same token tree for all derive macros in the same derive attribute. + derive_macro_id: MacroCallId, }, Attr { ast_id: AstId, @@ -484,7 +486,7 @@ impl MacroDefId { matches!( self.kind, MacroDefKind::BuiltIn(..) - | MacroDefKind::ProcMacro(_, ProcMacroKind::FuncLike, _) + | MacroDefKind::ProcMacro(_, ProcMacroKind::Bang, _) | MacroDefKind::BuiltInEager(..) | MacroDefKind::Declarative(..) ) @@ -806,7 +808,8 @@ impl ExpansionInfo { let (parse, exp_map) = db.parse_macro_expansion(macro_file).value; let expanded = InMacroFile { file_id: macro_file, value: parse.syntax_node() }; - let (macro_arg, _, _) = db.macro_arg(macro_file.macro_call_id); + let (macro_arg, _, _) = + db.macro_arg_considering_derives(macro_file.macro_call_id, &loc.kind); let def = loc.def.ast_id().left().and_then(|id| { let def_tt = match id.to_node(db) { 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 fc186d2c26d35..46f8c2b9d8c4c 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 @@ -225,6 +225,26 @@ fn convert_path( let mut segments = path.segments(); let segment = &segments.next()?; + let handle_super_kw = &mut |init_deg| { + let mut deg = init_deg; + let mut next_segment = None; + for segment in segments.by_ref() { + match segment.kind()? { + ast::PathSegmentKind::SuperKw => deg += 1, + ast::PathSegmentKind::Name(name) => { + next_segment = Some(name.as_name()); + break; + } + ast::PathSegmentKind::Type { .. } + | ast::PathSegmentKind::SelfTypeKw + | ast::PathSegmentKind::SelfKw + | ast::PathSegmentKind::CrateKw => return None, + } + } + + Some(ModPath::from_segments(PathKind::Super(deg), next_segment)) + }; + let mut mod_path = match segment.kind()? { ast::PathSegmentKind::Name(name_ref) => { if name_ref.text() == "$crate" { @@ -245,26 +265,8 @@ fn convert_path( ModPath::from_segments(PathKind::Plain, Some(known::SELF_TYPE)) } ast::PathSegmentKind::CrateKw => ModPath::from_segments(PathKind::Crate, iter::empty()), - ast::PathSegmentKind::SelfKw => ModPath::from_segments(PathKind::Super(0), iter::empty()), - ast::PathSegmentKind::SuperKw => { - let mut deg = 1; - let mut next_segment = None; - for segment in segments.by_ref() { - match segment.kind()? { - ast::PathSegmentKind::SuperKw => deg += 1, - ast::PathSegmentKind::Name(name) => { - next_segment = Some(name.as_name()); - break; - } - ast::PathSegmentKind::Type { .. } - | ast::PathSegmentKind::SelfTypeKw - | ast::PathSegmentKind::SelfKw - | ast::PathSegmentKind::CrateKw => return None, - } - } - - ModPath::from_segments(PathKind::Super(deg), next_segment) - } + ast::PathSegmentKind::SelfKw => handle_super_kw(0)?, + ast::PathSegmentKind::SuperKw => handle_super_kw(1)?, ast::PathSegmentKind::Type { .. } => { // not allowed in imports return None; 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 ca6fc0afe2d7d..abed16fecdebc 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,7 +23,7 @@ impl ProcMacroId { #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] pub enum ProcMacroKind { CustomDerive, - FuncLike, + Bang, Attr, } diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index 3cfedcdcb4dc7..bf473740166c8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml @@ -47,13 +47,14 @@ hir-expand.workspace = true base-db.workspace = true syntax.workspace = true limit.workspace = true +span.workspace = true [dev-dependencies] expect-test = "1.4.0" tracing.workspace = true tracing-subscriber.workspace = true tracing-tree.workspace = true -project-model = { path = "../project-model" } +project-model.workspace = true # local deps test-utils.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs index c485c9b2e80b3..cb118a3684860 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs @@ -9,21 +9,21 @@ use chalk_ir::{ AdtId, DebruijnIndex, Scalar, }; use hir_def::{ - builtin_type::BuiltinType, generics::TypeOrConstParamData, ConstParamId, DefWithBodyId, - GenericDefId, TraitId, TypeAliasId, + builtin_type::BuiltinType, DefWithBodyId, GenericDefId, GenericParamId, TraitId, TypeAliasId, }; use smallvec::SmallVec; use crate::{ - consteval::unknown_const_as_generic, db::HirDatabase, infer::unify::InferenceTable, primitive, - to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, BoundVar, CallableSig, - GenericArg, GenericArgData, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt, - TyKind, + consteval::unknown_const_as_generic, db::HirDatabase, error_lifetime, + infer::unify::InferenceTable, primitive, to_assoc_type_id, to_chalk_trait_id, utils::generics, + Binders, BoundVar, CallableSig, GenericArg, GenericArgData, Interner, ProjectionTy, + Substitution, TraitRef, Ty, TyDefId, TyExt, TyKind, }; #[derive(Debug, Clone, PartialEq, Eq)] pub enum ParamKind { Type, + Lifetime, Const(Ty), } @@ -107,6 +107,9 @@ impl TyBuilder { ParamKind::Const(ty) => { BoundVar::new(debruijn, idx).to_const(Interner, ty.clone()).cast(Interner) } + ParamKind::Lifetime => { + BoundVar::new(debruijn, idx).to_lifetime(Interner).cast(Interner) + } }); this.vec.extend(filler.take(this.remaining()).casted(Interner)); assert_eq!(this.remaining(), 0); @@ -119,6 +122,7 @@ impl TyBuilder { let filler = this.param_kinds[this.vec.len()..].iter().map(|x| match x { ParamKind::Type => TyKind::Error.intern(Interner).cast(Interner), ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), + ParamKind::Lifetime => error_lifetime().cast(Interner), }); this.vec.extend(filler.casted(Interner)); assert_eq!(this.remaining(), 0); @@ -130,6 +134,7 @@ impl TyBuilder { self.fill(|x| match x { ParamKind::Type => table.new_type_var().cast(Interner), ParamKind::Const(ty) => table.new_const_var(ty.clone()).cast(Interner), + ParamKind::Lifetime => table.new_lifetime_var().cast(Interner), }) } @@ -142,7 +147,8 @@ impl TyBuilder { fn assert_match_kind(&self, a: &chalk_ir::GenericArg, e: &ParamKind) { match (a.data(Interner), e) { (GenericArgData::Ty(_), ParamKind::Type) - | (GenericArgData::Const(_), ParamKind::Const(_)) => (), + | (GenericArgData::Const(_), ParamKind::Const(_)) + | (GenericArgData::Lifetime(_), ParamKind::Lifetime) => (), _ => panic!("Mismatched kinds: {a:?}, {:?}, {:?}", self.vec, self.param_kinds), } } @@ -201,10 +207,11 @@ impl TyBuilder<()> { Substitution::from_iter( Interner, params.iter_id().map(|id| match id { - either::Either::Left(_) => TyKind::Error.intern(Interner).cast(Interner), - either::Either::Right(id) => { + GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner), + GenericParamId::ConstParamId(id) => { unknown_const_as_generic(db.const_param_ty(id)).cast(Interner) } + GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner), }), ) } @@ -219,11 +226,10 @@ impl TyBuilder<()> { assert!(generics.parent_generics().is_some() == parent_subst.is_some()); let params = generics .iter_self() - .map(|(id, data)| match data { - TypeOrConstParamData::TypeParamData(_) => ParamKind::Type, - TypeOrConstParamData::ConstParamData(_) => { - ParamKind::Const(db.const_param_ty(ConstParamId::from_unchecked(id))) - } + .map(|(id, _data)| match id { + GenericParamId::TypeParamId(_) => ParamKind::Type, + GenericParamId::ConstParamId(id) => ParamKind::Const(db.const_param_ty(id)), + GenericParamId::LifetimeParamId(_) => ParamKind::Lifetime, }) .collect(); TyBuilder::new((), params, parent_subst) 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 e678a2fee1321..46612242b090c 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 @@ -272,6 +272,19 @@ impl chalk_solve::RustIrDatabase for ChalkContext<'_> { }; chalk_ir::Binders::new(binders, bound) } + crate::ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + let datas = self + .db + .type_alias_impl_traits(alias) + .expect("impl trait id without impl traits"); + let (datas, binders) = (*datas).as_ref().into_value_and_skipped_binders(); + let data = &datas.impl_traits[idx]; + let bound = OpaqueTyDatumBound { + bounds: make_single_type_binders(data.bounds.skip_binders().to_vec()), + where_clauses: chalk_ir::Binders::empty(Interner, vec![]), + }; + chalk_ir::Binders::new(binders, bound) + } crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => { if let Some((future_trait, future_output)) = self .db 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 795a5996912b2..d1aebeff261c5 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 @@ -268,6 +268,13 @@ impl TyExt for Ty { data.substitute(Interner, &subst).into_value_and_skipped_binders().0 }) } + ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + db.type_alias_impl_traits(alias).map(|it| { + let data = + (*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); + data.substitute(Interner, &subst).into_value_and_skipped_binders().0 + }) + } } } TyKind::Alias(AliasTy::Opaque(opaque_ty)) => { @@ -280,6 +287,13 @@ impl TyExt for Ty { data.substitute(Interner, &opaque_ty.substitution) }) } + ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + db.type_alias_impl_traits(alias).map(|it| { + let data = + (*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); + data.substitute(Interner, &opaque_ty.substitution) + }) + } // It always has an parameter for Future::Output type. ImplTraitId::AsyncBlockTypeImplTrait(..) => unreachable!(), }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs index 98384c47490a9..d1ffd5046c3f3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs @@ -2825,3 +2825,30 @@ fn unsized_local() { |e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::UnsizedTemporary(_))), ); } + +#[test] +fn recursive_adt() { + check_fail( + r#" + //- minicore: coerce_unsized, index, slice + pub enum TagTree { + Leaf, + Choice(&'static [TagTree]), + } + const GOAL: TagTree = { + const TAG_TREE: TagTree = TagTree::Choice(&[ + { + const VARIANT_TAG_TREE: TagTree = TagTree::Choice( + &[ + TagTree::Leaf, + ], + ); + VARIANT_TAG_TREE + }, + ]); + TAG_TREE + }; + "#, + |e| matches!(e, ConstEvalError::MirEvalError(MirEvalError::StackOverflow)), + ); +} 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 28c497989fe98..90bf46b5056cc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -11,7 +11,7 @@ use base_db::{ use hir_def::{ db::DefDatabase, hir::ExprId, layout::TargetDataLayout, AdtId, BlockId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId, - LifetimeParamId, LocalFieldId, StaticId, TypeOrConstParamId, VariantId, + LifetimeParamId, LocalFieldId, StaticId, TypeAliasId, TypeOrConstParamId, VariantId, }; use la_arena::ArenaMap; use smallvec::SmallVec; @@ -23,9 +23,9 @@ use crate::{ layout::{Layout, LayoutError}, method_resolution::{InherentImpls, TraitImpls, TyFingerprint}, mir::{BorrowckResult, MirBody, MirLowerError}, - Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult, - Interner, PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, - TraitEnvironment, TraitRef, Ty, TyDefId, ValueTyDefId, + Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, ImplTraits, + InferenceResult, Interner, PolyFnSig, QuantifiedWhereClause, Substitution, TraitEnvironment, + TraitRef, Ty, TyDefId, ValueTyDefId, }; use hir_expand::name::Name; @@ -132,10 +132,10 @@ pub trait HirDatabase: DefDatabase + Upcast { fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig; #[salsa::invoke(crate::lower::return_type_impl_traits)] - fn return_type_impl_traits( - &self, - def: FunctionId, - ) -> Option>>; + fn return_type_impl_traits(&self, def: FunctionId) -> Option>>; + + #[salsa::invoke(crate::lower::type_alias_impl_traits)] + fn type_alias_impl_traits(&self, def: TypeAliasId) -> Option>>; #[salsa::invoke(crate::lower::generic_predicates_for_param_query)] #[salsa::cycle(crate::lower::generic_predicates_for_param_recover)] 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 67cfbc294df7f..20b0da441daa8 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 @@ -11,7 +11,6 @@ use hir_def::{ItemContainerId, Lookup}; use hir_expand::name; use itertools::Itertools; use rustc_hash::FxHashSet; -use rustc_pattern_analysis::usefulness::{compute_match_usefulness, ValidityConstraint}; use syntax::{ast, AstNode}; use tracing::debug; use triomphe::Arc; @@ -234,13 +233,7 @@ impl ExprValidator { return; } - let report = match compute_match_usefulness( - &cx, - m_arms.as_slice(), - scrut_ty.clone(), - ValidityConstraint::ValidOnly, - None, - ) { + let report = match cx.compute_match_usefulness(m_arms.as_slice(), scrut_ty.clone()) { Ok(report) => report, Err(()) => return, }; @@ -282,13 +275,7 @@ impl ExprValidator { continue; } - let report = match compute_match_usefulness( - &cx, - &[match_arm], - ty.clone(), - ValidityConstraint::ValidOnly, - None, - ) { + let report = match cx.compute_match_usefulness(&[match_arm], ty.clone()) { Ok(v) => v, Err(e) => { debug!(?e, "match usefulness error"); 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 ca05842879657..f45beb4c92bfa 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 @@ -8,7 +8,8 @@ use rustc_hash::FxHashMap; use rustc_pattern_analysis::{ constructor::{Constructor, ConstructorSet, VariantVisibility}, index::IdxContainer, - Captures, PrivateUninhabitedField, TypeCx, + usefulness::{compute_match_usefulness, PlaceValidity, UsefulnessReport}, + Captures, PatCx, PrivateUninhabitedField, }; use smallvec::{smallvec, SmallVec}; use stdx::never; @@ -59,6 +60,18 @@ impl<'p> MatchCheckCtx<'p> { Self { module, body, db, exhaustive_patterns, min_exhaustive_patterns } } + pub(crate) fn compute_match_usefulness( + &self, + arms: &[MatchArm<'p>], + scrut_ty: Ty, + ) -> Result, ()> { + // FIXME: Determine place validity correctly. For now, err on the safe side. + let place_validity = PlaceValidity::MaybeInvalid; + // Measured to take ~100ms on modern hardware. + let complexity_limit = Some(500000); + compute_match_usefulness(self, arms, scrut_ty, place_validity, complexity_limit) + } + fn is_uninhabited(&self, ty: &Ty) -> bool { is_ty_uninhabited_from(ty, self.module, self.db) } @@ -107,15 +120,17 @@ impl<'p> MatchCheckCtx<'p> { } pub(crate) fn lower_pat(&self, pat: &Pat) -> DeconstructedPat<'p> { - let singleton = |pat| vec![pat]; + let singleton = |pat: DeconstructedPat<'p>| vec![pat.at_index(0)]; let ctor; - let fields: Vec<_>; + let mut fields: Vec<_>; + let arity; match pat.kind.as_ref() { PatKind::Binding { subpattern: Some(subpat), .. } => return self.lower_pat(subpat), PatKind::Binding { subpattern: None, .. } | PatKind::Wild => { ctor = Wildcard; fields = Vec::new(); + arity = 0; } PatKind::Deref { subpattern } => { ctor = match pat.ty.kind(Interner) { @@ -128,23 +143,22 @@ impl<'p> MatchCheckCtx<'p> { } }; fields = singleton(self.lower_pat(subpattern)); + arity = 1; } PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => { + fields = subpatterns + .iter() + .map(|pat| { + let idx: u32 = pat.field.into_raw().into(); + self.lower_pat(&pat.pattern).at_index(idx as usize) + }) + .collect(); match pat.ty.kind(Interner) { TyKind::Tuple(_, substs) => { ctor = Struct; - let mut wilds: Vec<_> = substs - .iter(Interner) - .map(|arg| arg.assert_ty_ref(Interner).clone()) - .map(DeconstructedPat::wildcard) - .collect(); - for pat in subpatterns { - let idx: u32 = pat.field.into_raw().into(); - wilds[idx as usize] = self.lower_pat(&pat.pattern); - } - fields = wilds + arity = substs.len(Interner); } - TyKind::Adt(adt, substs) if is_box(self.db, adt.0) => { + TyKind::Adt(adt, _) if is_box(self.db, adt.0) => { // The only legal patterns of type `Box` (outside `std`) are `_` and box // patterns. If we're here we can assume this is a box pattern. // FIXME(Nadrieril): A `Box` can in theory be matched either with `Box(_, @@ -157,16 +171,9 @@ impl<'p> MatchCheckCtx<'p> { // normally or through box-patterns. We'll have to figure out a proper // solution when we introduce generalized deref patterns. Also need to // prevent mixing of those two options. - let pat = - subpatterns.iter().find(|pat| pat.field.into_raw() == 0u32.into()); - let field = if let Some(pat) = pat { - self.lower_pat(&pat.pattern) - } else { - let ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone(); - DeconstructedPat::wildcard(ty) - }; + fields.retain(|ipat| ipat.idx == 0); ctor = Struct; - fields = singleton(field); + arity = 1; } &TyKind::Adt(adt, _) => { ctor = match pat.kind.as_ref() { @@ -181,37 +188,33 @@ impl<'p> MatchCheckCtx<'p> { } }; let variant = Self::variant_id_for_adt(&ctor, adt.0).unwrap(); - // Fill a vec with wildcards, then place the fields we have at the right - // index. - let mut wilds: Vec<_> = self - .list_variant_fields(&pat.ty, variant) - .map(|(_, ty)| ty) - .map(DeconstructedPat::wildcard) - .collect(); - for pat in subpatterns { - let field_id: u32 = pat.field.into_raw().into(); - wilds[field_id as usize] = self.lower_pat(&pat.pattern); - } - fields = wilds; + arity = variant.variant_data(self.db.upcast()).fields().len(); } _ => { never!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, &pat.ty); ctor = Wildcard; - fields = Vec::new(); + fields.clear(); + arity = 0; } } } &PatKind::LiteralBool { value } => { ctor = Bool(value); fields = Vec::new(); + arity = 0; } PatKind::Or { pats } => { ctor = Or; - fields = pats.iter().map(|pat| self.lower_pat(pat)).collect(); + fields = pats + .iter() + .enumerate() + .map(|(i, pat)| self.lower_pat(pat).at_index(i)) + .collect(); + arity = pats.len(); } } let data = PatData { db: self.db }; - DeconstructedPat::new(ctor, fields, pat.ty.clone(), data) + DeconstructedPat::new(ctor, fields, arity, pat.ty.clone(), data) } pub(crate) fn hoist_witness_pat(&self, pat: &WitnessPat<'p>) -> Pat { @@ -271,7 +274,7 @@ impl<'p> MatchCheckCtx<'p> { } } -impl<'p> TypeCx for MatchCheckCtx<'p> { +impl<'p> PatCx for MatchCheckCtx<'p> { type Error = (); type Ty = Ty; type VariantIdx = EnumVariantId; @@ -453,7 +456,7 @@ impl<'p> TypeCx for MatchCheckCtx<'p> { let variant = pat.ty().as_adt().and_then(|(adt, _)| Self::variant_id_for_adt(pat.ctor(), adt)); - let db = pat.data().unwrap().db; + let db = pat.data().db; if let Some(variant) = variant { match variant { VariantId::EnumVariantId(v) => { @@ -475,7 +478,6 @@ impl<'p> TypeCx for MatchCheckCtx<'p> { } fn complexity_exceeded(&self) -> Result<(), Self::Error> { - // FIXME(Nadrieril): make use of the complexity counter. Err(()) } } 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 20964f5acbd01..8740ae6797cb1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -938,18 +938,32 @@ impl HirDisplay for Ty { f.end_location_link(); if parameters.len(Interner) > 0 { let generics = generics(db.upcast(), def.into()); - let (parent_params, self_param, type_params, const_params, _impl_trait_params) = - generics.provenance_split(); - let total_len = parent_params + self_param + type_params + const_params; + let ( + parent_params, + self_param, + type_params, + const_params, + _impl_trait_params, + lifetime_params, + ) = generics.provenance_split(); + let total_len = + parent_params + self_param + type_params + const_params + lifetime_params; // We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self? if total_len > 0 { - // `parameters` are in the order of fn's params (including impl traits), + // `parameters` are in the order of fn's params (including impl traits), fn's lifetimes // parent's params (those from enclosing impl or trait, if any). let parameters = parameters.as_slice(Interner); let fn_params_len = self_param + type_params + const_params; + // This will give slice till last type or const let fn_params = parameters.get(..fn_params_len); + let fn_lt_params = + parameters.get(fn_params_len..(fn_params_len + lifetime_params)); let parent_params = parameters.get(parameters.len() - parent_params..); - let params = parent_params.into_iter().chain(fn_params).flatten(); + let params = parent_params + .into_iter() + .chain(fn_lt_params) + .chain(fn_params) + .flatten(); write!(f, "<")?; f.write_joined(params, ", ")?; write!(f, ">")?; @@ -1063,6 +1077,20 @@ impl HirDisplay for Ty { )?; // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution } + ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + let datas = + db.type_alias_impl_traits(alias).expect("impl trait id without data"); + let data = + (*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); + let bounds = data.substitute(Interner, ¶meters); + let krate = alias.krate(db.upcast()); + write_bounds_like_dyn_trait_with_prefix( + f, + "impl", + bounds.skip_binders(), + SizedByDefault::Sized { anchor: krate }, + )?; + } ImplTraitId::AsyncBlockTypeImplTrait(body, ..) => { let future_trait = db .lang_item(body.module(db.upcast()).krate(), LangItem::Future) @@ -1228,6 +1256,20 @@ impl HirDisplay for Ty { SizedByDefault::Sized { anchor: krate }, )?; } + ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + let datas = + db.type_alias_impl_traits(alias).expect("impl trait id without data"); + let data = + (*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); + let bounds = data.substitute(Interner, &opaque_ty.substitution); + let krate = alias.krate(db.upcast()); + write_bounds_like_dyn_trait_with_prefix( + f, + "impl", + bounds.skip_binders(), + SizedByDefault::Sized { anchor: krate }, + )?; + } ImplTraitId::AsyncBlockTypeImplTrait(..) => { write!(f, "{{async block}}")?; } @@ -1280,8 +1322,17 @@ fn hir_fmt_generics( generic_def: Option, ) -> Result<(), HirDisplayError> { let db = f.db; - let lifetime_args_count = generic_def.map_or(0, |g| db.generic_params(g).lifetimes.len()); - if parameters.len(Interner) + lifetime_args_count > 0 { + if parameters.len(Interner) > 0 { + use std::cmp::Ordering; + let param_compare = + |a: &GenericArg, b: &GenericArg| match (a.data(Interner), b.data(Interner)) { + (crate::GenericArgData::Lifetime(_), crate::GenericArgData::Lifetime(_)) => { + Ordering::Equal + } + (crate::GenericArgData::Lifetime(_), _) => Ordering::Less, + (_, crate::GenericArgData::Lifetime(_)) => Ordering::Less, + (_, _) => Ordering::Equal, + }; let parameters_to_write = if f.display_target.is_source_code() || f.omit_verbose_types() { match generic_def .map(|generic_def_id| db.generic_defaults(generic_def_id)) @@ -1307,6 +1358,11 @@ fn hir_fmt_generics( return true; } } + if parameter.lifetime(Interner).map(|it| it.data(Interner)) + == Some(&crate::LifetimeData::Static) + { + return true; + } let default_parameter = match default_parameters.get(i) { Some(it) => it, None => return true, @@ -1327,16 +1383,12 @@ fn hir_fmt_generics( } else { parameters.as_slice(Interner) }; - if !parameters_to_write.is_empty() || lifetime_args_count != 0 { + //FIXME: Should handle the ordering of lifetimes when creating substitutions + let mut parameters_to_write = parameters_to_write.to_vec(); + parameters_to_write.sort_by(param_compare); + if !parameters_to_write.is_empty() { write!(f, "<")?; let mut first = true; - for _ in 0..lifetime_args_count { - if !first { - write!(f, ", ")?; - } - first = false; - write!(f, "'_")?; - } for generic_arg in parameters_to_write { if !first { write!(f, ", ")?; 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 34ba17f145e0a..be3b50e1411da 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -25,8 +25,11 @@ pub(crate) mod unify; use std::{convert::identity, iter, ops::Index}; use chalk_ir::{ - cast::Cast, fold::TypeFoldable, interner::HasInterner, DebruijnIndex, Mutability, Safety, - Scalar, TyKind, TypeFlags, Variance, + cast::Cast, + fold::TypeFoldable, + interner::HasInterner, + visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, + DebruijnIndex, Mutability, Safety, Scalar, TyKind, TypeFlags, Variance, }; use either::Either; use hir_def::{ @@ -39,7 +42,7 @@ use hir_def::{ layout::Integer, path::{ModPath, Path}, resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs}, - type_ref::TypeRef, + type_ref::{LifetimeRef, TypeRef}, AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, ItemContainerId, Lookup, TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId, }; @@ -53,14 +56,14 @@ use triomphe::Arc; use crate::{ db::HirDatabase, fold_tys, - infer::coerce::CoerceMany, + infer::{coerce::CoerceMany, unify::InferenceTable}, lower::ImplTraitLoweringMode, static_lifetime, to_assoc_type_id, traits::FnTrait, utils::{InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder}, AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, Goal, ImplTraitId, - InEnvironment, Interner, Lifetime, ProjectionTy, RpitId, Substitution, TraitEnvironment, - TraitRef, Ty, TyBuilder, TyExt, + ImplTraitIdx, InEnvironment, Interner, Lifetime, OpaqueTyId, ProjectionTy, Substitution, + TraitEnvironment, Ty, TyBuilder, TyExt, }; // This lint has a false positive here. See the link below for details. @@ -422,7 +425,7 @@ pub struct InferenceResult { /// unresolved or missing subpatterns or subpatterns of mismatched types. pub type_of_pat: ArenaMap, pub type_of_binding: ArenaMap, - pub type_of_rpit: ArenaMap, + pub type_of_rpit: ArenaMap, /// Type of the result of `.into_iter()` on the for. `ExprId` is the one of the whole for loop. pub type_of_for_iterator: FxHashMap, type_mismatches: FxHashMap, @@ -752,7 +755,12 @@ impl<'a> InferenceContext<'a> { } fn collect_const(&mut self, data: &ConstData) { - self.return_ty = self.make_ty(&data.type_ref); + let return_ty = self.make_ty(&data.type_ref); + + // Constants might be associated items that define ATPITs. + self.insert_atpit_coercion_table(iter::once(&return_ty)); + + self.return_ty = return_ty; } fn collect_static(&mut self, data: &StaticData) { @@ -785,11 +793,13 @@ impl<'a> InferenceContext<'a> { self.write_binding_ty(self_param, ty); } } + let mut params_and_ret_tys = Vec::new(); for (ty, pat) in param_tys.zip(&*self.body.params) { let ty = self.insert_type_vars(ty); let ty = self.normalize_associated_types_in(ty); self.infer_top_pat(*pat, &ty); + params_and_ret_tys.push(ty); } let return_ty = &*data.ret_type; @@ -801,8 +811,11 @@ impl<'a> InferenceContext<'a> { let return_ty = if let Some(rpits) = self.db.return_type_impl_traits(func) { // RPIT opaque types use substitution of their parent function. let fn_placeholders = TyBuilder::placeholder_subst(self.db, func); - let result = - self.insert_inference_vars_for_rpit(return_ty, rpits.clone(), fn_placeholders); + let result = self.insert_inference_vars_for_impl_trait( + return_ty, + rpits.clone(), + fn_placeholders, + ); let rpits = rpits.skip_binders(); for (id, _) in rpits.impl_traits.iter() { if let Entry::Vacant(e) = self.result.type_of_rpit.entry(id) { @@ -817,13 +830,19 @@ impl<'a> InferenceContext<'a> { self.return_ty = self.normalize_associated_types_in(return_ty); self.return_coercion = Some(CoerceMany::new(self.return_ty.clone())); + + // Functions might be associated items that define ATPITs. + // To define an ATPITs, that ATPIT must appear in the function's signatures. + // So, it suffices to check for params and return types. + params_and_ret_tys.push(self.return_ty.clone()); + self.insert_atpit_coercion_table(params_and_ret_tys.iter()); } - fn insert_inference_vars_for_rpit( + fn insert_inference_vars_for_impl_trait( &mut self, t: T, - rpits: Arc>, - fn_placeholders: Substitution, + rpits: Arc>, + placeholders: Substitution, ) -> T where T: crate::HasInterner + crate::TypeFoldable, @@ -837,6 +856,7 @@ impl<'a> InferenceContext<'a> { }; let idx = match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) { ImplTraitId::ReturnTypeImplTrait(_, idx) => idx, + ImplTraitId::AssociatedTypeImplTrait(_, idx) => idx, _ => unreachable!(), }; let bounds = @@ -844,15 +864,14 @@ impl<'a> InferenceContext<'a> { let var = self.table.new_type_var(); let var_subst = Substitution::from1(Interner, var.clone()); for bound in bounds { - let predicate = - bound.map(|it| it.cloned()).substitute(Interner, &fn_placeholders); + let predicate = bound.map(|it| it.cloned()).substitute(Interner, &placeholders); let (var_predicate, binders) = predicate.substitute(Interner, &var_subst).into_value_and_skipped_binders(); always!(binders.is_empty(Interner)); // quantified where clauses not yet handled - let var_predicate = self.insert_inference_vars_for_rpit( + let var_predicate = self.insert_inference_vars_for_impl_trait( var_predicate, rpits.clone(), - fn_placeholders.clone(), + placeholders.clone(), ); self.push_obligation(var_predicate.cast(Interner)); } @@ -863,6 +882,106 @@ impl<'a> InferenceContext<'a> { ) } + /// The coercion of a non-inference var into an opaque type should fail, + /// but not in the defining sites of the ATPITs. + /// In such cases, we insert an proxy inference var for each ATPIT, + /// and coerce into it instead of ATPIT itself. + /// + /// The inference var stretagy is effective because; + /// + /// - It can still unify types that coerced into ATPIT + /// - We are pushing `impl Trait` bounds into it + /// + /// This function inserts a map that maps the opaque type to that proxy inference var. + fn insert_atpit_coercion_table<'b>(&mut self, tys: impl Iterator) { + struct OpaqueTyCollector<'a, 'b> { + table: &'b mut InferenceTable<'a>, + opaque_tys: FxHashMap, + } + + impl<'a, 'b> TypeVisitor for OpaqueTyCollector<'a, 'b> { + type BreakTy = (); + + fn as_dyn(&mut self) -> &mut dyn TypeVisitor { + self + } + + fn interner(&self) -> Interner { + Interner + } + + fn visit_ty( + &mut self, + ty: &chalk_ir::Ty, + outer_binder: DebruijnIndex, + ) -> std::ops::ControlFlow { + let ty = self.table.resolve_ty_shallow(ty); + + if let TyKind::OpaqueType(id, _) = ty.kind(Interner) { + self.opaque_tys.insert(*id, ty.clone()); + } + + ty.super_visit_with(self, outer_binder) + } + } + + // Early return if this is not happening inside the impl block + let impl_id = if let Some(impl_id) = self.resolver.impl_def() { + impl_id + } else { + return; + }; + + let assoc_tys: FxHashSet<_> = self + .db + .impl_data(impl_id) + .items + .iter() + .filter_map(|item| match item { + AssocItemId::TypeAliasId(alias) => Some(*alias), + _ => None, + }) + .collect(); + if assoc_tys.is_empty() { + return; + } + + let mut collector = + OpaqueTyCollector { table: &mut self.table, opaque_tys: FxHashMap::default() }; + for ty in tys { + ty.visit_with(collector.as_dyn(), DebruijnIndex::INNERMOST); + } + let atpit_coercion_table: FxHashMap<_, _> = collector + .opaque_tys + .into_iter() + .filter_map(|(opaque_ty_id, ty)| { + if let ImplTraitId::AssociatedTypeImplTrait(alias_id, _) = + self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) + { + if assoc_tys.contains(&alias_id) { + let atpits = self + .db + .type_alias_impl_traits(alias_id) + .expect("Marked as ATPIT but no impl traits!"); + let alias_placeholders = TyBuilder::placeholder_subst(self.db, alias_id); + let ty = self.insert_inference_vars_for_impl_trait( + ty, + atpits, + alias_placeholders, + ); + return Some((opaque_ty_id, ty)); + } + } + + None + }) + .collect(); + + if !atpit_coercion_table.is_empty() { + self.table.atpit_coercion_table = Some(atpit_coercion_table); + } + } + fn infer_body(&mut self) { match self.return_coercion { Some(_) => self.infer_return(self.body.body_expr), @@ -918,6 +1037,12 @@ impl<'a> InferenceContext<'a> { self.result.standard_types.unknown.clone() } + fn make_lifetime(&mut self, lifetime_ref: &LifetimeRef) -> Lifetime { + let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into()); + let lt = ctx.lower_lifetime(lifetime_ref); + self.insert_type_vars(lt) + } + /// Replaces `Ty::Error` by a new type var, so we can maybe still infer it. fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty { self.table.insert_type_vars_shallow(ty) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index ff6de61ba6493..cfbbc9dd6c04f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -276,6 +276,23 @@ impl InferenceTable<'_> { return success(simple(Adjust::NeverToAny)(to_ty.clone()), to_ty.clone(), vec![]); } + // If we are coercing into an ATPIT, coerce into its proxy inference var, instead. + let mut to_ty = to_ty; + let _to; + if let Some(atpit_table) = &self.atpit_coercion_table { + if let TyKind::OpaqueType(opaque_ty_id, _) = to_ty.kind(Interner) { + if !matches!( + from_ty.kind(Interner), + TyKind::InferenceVar(..) | TyKind::OpaqueType(..) + ) { + if let Some(ty) = atpit_table.get(opaque_ty_id) { + _to = ty.clone(); + to_ty = &_to; + } + } + } + } + // Consider coercing the subtype to a DST if let Ok(ret) = self.try_coerce_unsized(&from_ty, to_ty) { return Ok(ret); 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 a3dab1fd9d54c..35d59679355a6 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 @@ -8,13 +8,12 @@ use std::{ use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKind}; use either::Either; use hir_def::{ - generics::TypeOrConstParamData, hir::{ ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp, }, lang_item::{LangItem, LangItemTarget}, - path::{GenericArg, GenericArgs, Path}, - BlockId, ConstParamId, FieldId, ItemContainerId, Lookup, TupleFieldId, TupleId, + path::{GenericArgs, Path}, + BlockId, FieldId, GenericParamId, ItemContainerId, Lookup, TupleFieldId, TupleId, }; use hir_expand::name::{name, Name}; use stdx::always; @@ -1816,10 +1815,17 @@ impl InferenceContext<'_> { def_generics: Generics, generic_args: Option<&GenericArgs>, ) -> Substitution { - let (parent_params, self_params, type_params, const_params, impl_trait_params) = - def_generics.provenance_split(); + let ( + parent_params, + self_params, + type_params, + const_params, + impl_trait_params, + lifetime_params, + ) = def_generics.provenance_split(); assert_eq!(self_params, 0); // method shouldn't have another Self param - let total_len = parent_params + type_params + const_params + impl_trait_params; + let total_len = + parent_params + type_params + const_params + impl_trait_params + lifetime_params; let mut substs = Vec::with_capacity(total_len); // handle provided arguments @@ -1828,8 +1834,7 @@ impl InferenceContext<'_> { for (arg, kind_id) in generic_args .args .iter() - .filter(|arg| !matches!(arg, GenericArg::Lifetime(_))) - .take(type_params + const_params) + .take(type_params + const_params + lifetime_params) .zip(def_generics.iter_id()) { if let Some(g) = generic_arg_to_chalk( @@ -1850,6 +1855,7 @@ impl InferenceContext<'_> { DebruijnIndex::INNERMOST, ) }, + |this, lt_ref| this.make_lifetime(lt_ref), ) { substs.push(g); } @@ -1858,16 +1864,17 @@ impl InferenceContext<'_> { // Handle everything else as unknown. This also handles generic arguments for the method's // parent (impl or trait), which should come after those for the method. - for (id, data) in def_generics.iter().skip(substs.len()) { - match data { - TypeOrConstParamData::TypeParamData(_) => { + for (id, _data) in def_generics.iter().skip(substs.len()) { + match id { + GenericParamId::TypeParamId(_) => { substs.push(self.table.new_type_var().cast(Interner)) } - TypeOrConstParamData::ConstParamData(_) => substs.push( - self.table - .new_const_var(self.db.const_param_ty(ConstParamId::from_unchecked(id))) - .cast(Interner), - ), + GenericParamId::ConstParamId(id) => { + substs.push(self.table.new_const_var(self.db.const_param_ty(id)).cast(Interner)) + } + GenericParamId::LifetimeParamId(_) => { + substs.push(self.table.new_lifetime_var().cast(Interner)) + } } } assert_eq!(substs.len(), total_len); 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 8f537bb448b9e..9a1835b625b3c 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 @@ -11,15 +11,15 @@ use stdx::never; use crate::{ builder::ParamKind, - consteval, + consteval, error_lifetime, method_resolution::{self, VisibleFromModule}, to_chalk_trait_id, utils::generics, - InferenceDiagnostic, Interner, Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, - ValueTyDefId, + InferenceDiagnostic, Interner, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, + TyKind, ValueTyDefId, }; -use super::{ExprOrPatId, InferenceContext, TraitRef}; +use super::{ExprOrPatId, InferenceContext}; impl InferenceContext<'_> { pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option { @@ -111,6 +111,7 @@ impl InferenceContext<'_> { it.next().unwrap_or_else(|| match x { ParamKind::Type => self.result.standard_types.unknown.clone().cast(Interner), ParamKind::Const(ty) => consteval::unknown_const_as_generic(ty.clone()), + ParamKind::Lifetime => error_lifetime().cast(Interner), }) }) .build(); 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 be7547f9bae59..afb89fe1e5b1c 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 @@ -10,16 +10,18 @@ use chalk_solve::infer::ParameterEnaVariableExt; use either::Either; use ena::unify::UnifyKey; use hir_expand::name; +use rustc_hash::FxHashMap; use smallvec::SmallVec; use triomphe::Arc; use super::{InferOk, InferResult, InferenceContext, TypeError}; use crate::{ - consteval::unknown_const, db::HirDatabase, fold_tys_and_consts, static_lifetime, - to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, - DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData, Guidance, InEnvironment, - InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution, - Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause, + consteval::unknown_const, db::HirDatabase, fold_generic_args, fold_tys_and_consts, + static_lifetime, to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical, + Const, ConstValue, DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData, + Guidance, InEnvironment, InferenceVar, Interner, Lifetime, OpaqueTyId, ParamKind, ProjectionTy, + ProjectionTyExt, Scalar, Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, + TyKind, VariableKind, WhereClause, }; impl InferenceContext<'_> { @@ -239,6 +241,7 @@ type ChalkInferenceTable = chalk_solve::infer::InferenceTable; pub(crate) struct InferenceTable<'a> { pub(crate) db: &'a dyn HirDatabase, pub(crate) trait_env: Arc, + pub(crate) atpit_coercion_table: Option>, var_unification_table: ChalkInferenceTable, type_variable_table: SmallVec<[TypeVariableFlags; 16]>, pending_obligations: Vec>>, @@ -258,6 +261,7 @@ impl<'a> InferenceTable<'a> { InferenceTable { db, trait_env, + atpit_coercion_table: None, var_unification_table: ChalkInferenceTable::new(), type_variable_table: SmallVec::new(), pending_obligations: Vec::new(), @@ -803,6 +807,7 @@ impl<'a> InferenceTable<'a> { .fill(|it| { let arg = match it { ParamKind::Type => self.new_type_var(), + ParamKind::Lifetime => unreachable!("Tuple with lifetime parameter"), ParamKind::Const(_) => unreachable!("Tuple with const parameter"), }; arg_tys.push(arg.clone()); @@ -857,11 +862,16 @@ impl<'a> InferenceTable<'a> { where T: HasInterner + TypeFoldable, { - fold_tys_and_consts( + fold_generic_args( ty, - |it, _| match it { - Either::Left(ty) => Either::Left(self.insert_type_vars_shallow(ty)), - Either::Right(c) => Either::Right(self.insert_const_vars_shallow(c)), + |arg, _| match arg { + GenericArgData::Ty(ty) => GenericArgData::Ty(self.insert_type_vars_shallow(ty)), + // FIXME: insert lifetime vars once LifetimeData::InferenceVar + // and specific error variant for lifetimes start being constructed + GenericArgData::Lifetime(lt) => GenericArgData::Lifetime(lt), + GenericArgData::Const(c) => { + GenericArgData::Const(self.insert_const_vars_shallow(c)) + } }, DebruijnIndex::INNERMOST, ) 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 9655981cc9ccd..dd949e26c2ab2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -389,6 +389,9 @@ pub fn layout_of_ty_query( let infer = db.infer(func.into()); return db.layout_of_ty(infer.type_of_rpit[idx].clone(), trait_env); } + crate::ImplTraitId::AssociatedTypeImplTrait(..) => { + return Err(LayoutError::NotImplemented); + } crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { return Err(LayoutError::NotImplemented) } 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 ec97bdc2c4343..ba64f5c8d7ea8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -15,7 +15,8 @@ extern crate rustc_abi; #[cfg(not(feature = "in-rust-tree"))] extern crate ra_ap_rustc_abi as rustc_abi; -// No need to use the in-tree one. +// Use the crates.io version unconditionally until the API settles enough that we can switch to +// using the in-tree one. extern crate ra_ap_rustc_pattern_analysis as rustc_pattern_analysis; mod builder; @@ -89,8 +90,8 @@ pub use lower::{ }; pub use mapping::{ from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx, - lt_from_placeholder_idx, to_assoc_type_id, to_chalk_trait_id, to_foreign_def_id, - to_placeholder_idx, + lt_from_placeholder_idx, lt_to_placeholder_idx, to_assoc_type_id, to_chalk_trait_id, + to_foreign_def_id, to_placeholder_idx, }; pub use method_resolution::check_orphan_rules; pub use traits::TraitEnvironment; @@ -334,11 +335,23 @@ pub(crate) fn make_binders_with_count>( generics: &Generics, value: T, ) -> Binders { - let it = generics.iter_id().take(count).map(|id| match id { - Either::Left(_) => None, - Either::Right(id) => Some(db.const_param_ty(id)), - }); - crate::make_type_and_const_binders(it, value) + let it = generics.iter_id().take(count); + + Binders::new( + VariableKinds::from_iter( + Interner, + it.map(|x| match x { + hir_def::GenericParamId::ConstParamId(id) => { + chalk_ir::VariableKind::Const(db.const_param_ty(id)) + } + hir_def::GenericParamId::TypeParamId(_) => { + chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General) + } + hir_def::GenericParamId::LifetimeParamId(_) => chalk_ir::VariableKind::Lifetime, + }), + ), + value, + ) } pub(crate) fn make_binders>( @@ -584,29 +597,34 @@ impl TypeFoldable for CallableSig { #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] pub enum ImplTraitId { - ReturnTypeImplTrait(hir_def::FunctionId, RpitId), + ReturnTypeImplTrait(hir_def::FunctionId, ImplTraitIdx), + AssociatedTypeImplTrait(hir_def::TypeAliasId, ImplTraitIdx), AsyncBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId), } impl_intern_value_trivial!(ImplTraitId); #[derive(Clone, PartialEq, Eq, Debug, Hash)] -pub struct ReturnTypeImplTraits { - pub(crate) impl_traits: Arena, +pub struct ImplTraits { + pub(crate) impl_traits: Arena, } -has_interner!(ReturnTypeImplTraits); +has_interner!(ImplTraits); #[derive(Clone, PartialEq, Eq, Debug, Hash)] -pub struct ReturnTypeImplTrait { +pub struct ImplTrait { pub(crate) bounds: Binders>, } -pub type RpitId = Idx; +pub type ImplTraitIdx = Idx; pub fn static_lifetime() -> Lifetime { LifetimeData::Static.intern(Interner) } +pub fn error_lifetime() -> Lifetime { + static_lifetime() +} + pub(crate) fn fold_free_vars + TypeFoldable>( t: T, for_ty: impl FnMut(BoundVar, DebruijnIndex) -> Ty, @@ -696,6 +714,55 @@ pub(crate) fn fold_tys_and_consts + TypeFold t.fold_with(&mut TyFolder(f), binders) } +pub(crate) fn fold_generic_args + TypeFoldable>( + t: T, + f: impl FnMut(GenericArgData, DebruijnIndex) -> GenericArgData, + binders: DebruijnIndex, +) -> T { + use chalk_ir::fold::{TypeFolder, TypeSuperFoldable}; + #[derive(chalk_derive::FallibleTypeFolder)] + #[has_interner(Interner)] + struct TyFolder GenericArgData>(F); + impl GenericArgData> TypeFolder + for TyFolder + { + fn as_dyn(&mut self) -> &mut dyn TypeFolder { + self + } + + fn interner(&self) -> Interner { + Interner + } + + fn fold_ty(&mut self, ty: Ty, outer_binder: DebruijnIndex) -> Ty { + let ty = ty.super_fold_with(self.as_dyn(), outer_binder); + self.0(GenericArgData::Ty(ty), outer_binder) + .intern(Interner) + .ty(Interner) + .unwrap() + .clone() + } + + fn fold_const(&mut self, c: Const, outer_binder: DebruijnIndex) -> Const { + self.0(GenericArgData::Const(c), outer_binder) + .intern(Interner) + .constant(Interner) + .unwrap() + .clone() + } + + fn fold_lifetime(&mut self, lt: Lifetime, outer_binder: DebruijnIndex) -> Lifetime { + let lt = lt.super_fold_with(self.as_dyn(), outer_binder); + self.0(GenericArgData::Lifetime(lt), outer_binder) + .intern(Interner) + .lifetime(Interner) + .unwrap() + .clone() + } + } + t.fold_with(&mut TyFolder(f), binders) +} + /// 'Canonicalizes' the `t` by replacing any errors with new variables. Also /// ensures there are no unbound variables or inference variables anywhere in /// the `t`. 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 dac20f2259717..25ccc84c13c85 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -24,17 +24,20 @@ use hir_def::{ data::adt::StructKind, expander::Expander, generics::{ - TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget, + GenericParamDataRef, TypeOrConstParamData, TypeParamProvenance, WherePredicate, + WherePredicateTypeTarget, }, lang_item::LangItem, nameres::MacroSubNs, path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments}, - resolver::{HasResolver, Resolver, TypeNs}, - type_ref::{ConstRef, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef}, + resolver::{HasResolver, LifetimeNs, Resolver, TypeNs}, + type_ref::{ + ConstRef, LifetimeRef, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef, + }, AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, - GenericDefId, HasModule, ImplId, InTypeConstLoc, ItemContainerId, LocalFieldId, Lookup, - ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId, TypeOwnerId, - TypeParamId, UnionId, VariantId, + GenericDefId, GenericParamId, HasModule, ImplId, InTypeConstLoc, ItemContainerId, LocalFieldId, + Lookup, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId, TypeOwnerId, + UnionId, VariantId, }; use hir_expand::{name::Name, ExpandResult}; use intern::Interned; @@ -52,18 +55,18 @@ use crate::{ unknown_const_as_generic, }, db::HirDatabase, - make_binders, - mapping::{from_chalk_trait_id, ToChalk}, + error_lifetime, 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, - utils::Generics, utils::{ - all_super_trait_refs, associated_type_by_name_including_super_traits, generics, + all_super_trait_refs, associated_type_by_name_including_super_traits, generics, Generics, InTypeConstIdMetadata, }, AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy, - FnAbi, FnPointer, FnSig, FnSubst, ImplTraitId, Interner, ParamKind, PolyFnSig, ProjectionTy, - QuantifiedWhereClause, QuantifiedWhereClauses, ReturnTypeImplTrait, ReturnTypeImplTraits, - Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause, + FnAbi, FnPointer, FnSig, FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime, + LifetimeData, ParamKind, PolyFnSig, ProjectionTy, QuantifiedWhereClause, + QuantifiedWhereClauses, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, + TyKind, WhereClause, }; #[derive(Debug)] @@ -76,7 +79,7 @@ enum ImplTraitLoweringState { /// we're grouping the mutable data (the counter and this field) together /// with the immutable context (the references to the DB and resolver). /// Splitting this up would be a possible fix. - Opaque(RefCell>), + Opaque(RefCell>), Param(Cell), Variable(Cell), Disallowed, @@ -275,9 +278,11 @@ impl<'a> TyLoweringContext<'a> { let inner_ty = self.lower_ty(inner); TyKind::Slice(inner_ty).intern(Interner) } - TypeRef::Reference(inner, _, mutability) => { + TypeRef::Reference(inner, lifetime, mutability) => { let inner_ty = self.lower_ty(inner); - let lifetime = static_lifetime(); + // FIXME: It should infer the eldided lifetimes instead of stubbing with static + let lifetime = + lifetime.as_ref().map_or_else(static_lifetime, |lr| self.lower_lifetime(lr)); TyKind::Ref(lower_to_chalk_mutability(*mutability), lifetime, inner_ty) .intern(Interner) } @@ -301,15 +306,18 @@ impl<'a> TyLoweringContext<'a> { TypeRef::ImplTrait(bounds) => { match &self.impl_trait_mode { ImplTraitLoweringState::Opaque(opaque_type_data) => { - let func = match self.resolver.generic_def() { - Some(GenericDefId::FunctionId(f)) => f, - _ => panic!("opaque impl trait lowering in non-function"), + let origin = match self.resolver.generic_def() { + Some(GenericDefId::FunctionId(it)) => Either::Left(it), + Some(GenericDefId::TypeAliasId(it)) => Either::Right(it), + _ => panic!( + "opaque impl trait lowering must be in function or type alias" + ), }; // this dance is to make sure the data is in the right // place even if we encounter more opaque types while // lowering the bounds - let idx = opaque_type_data.borrow_mut().alloc(ReturnTypeImplTrait { + let idx = opaque_type_data.borrow_mut().alloc(ImplTrait { bounds: crate::make_single_type_binders(Vec::new()), }); // We don't want to lower the bounds inside the binders @@ -323,13 +331,17 @@ impl<'a> TyLoweringContext<'a> { // away instead of two. let actual_opaque_type_data = self .with_debruijn(DebruijnIndex::INNERMOST, |ctx| { - ctx.lower_impl_trait(bounds, func) + ctx.lower_impl_trait(bounds, self.resolver.krate()) }); opaque_type_data.borrow_mut()[idx] = actual_opaque_type_data; - let impl_trait_id = ImplTraitId::ReturnTypeImplTrait(func, idx); + let impl_trait_id = origin.either( + |f| ImplTraitId::ReturnTypeImplTrait(f, idx), + |a| ImplTraitId::AssociatedTypeImplTrait(a, idx), + ); let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into(); - let generics = generics(self.db.upcast(), func.into()); + let generics = + generics(self.db.upcast(), origin.either(|f| f.into(), |a| a.into())); let parameters = generics.bound_vars_subst(self.db, self.in_binders); TyKind::OpaqueType(opaque_ty_id, parameters).intern(Interner) } @@ -344,13 +356,18 @@ impl<'a> TyLoweringContext<'a> { .filter(|(_, data)| { matches!( data, - TypeOrConstParamData::TypeParamData(data) + GenericParamDataRef::TypeParamData(data) if data.provenance == TypeParamProvenance::ArgumentImplTrait ) }) .nth(idx as usize) .map_or(TyKind::Error, |(id, _)| { - TyKind::Placeholder(to_placeholder_idx(self.db, id)) + if let GenericParamId::TypeParamId(id) = id { + TyKind::Placeholder(to_placeholder_idx(self.db, id.into())) + } else { + // we just filtered them out + unreachable!("Unexpected lifetime or const argument"); + } }); param.intern(Interner) } else { @@ -367,11 +384,12 @@ impl<'a> TyLoweringContext<'a> { list_params, const_params, _impl_trait_params, + _lifetime_params, ) = if let Some(def) = self.resolver.generic_def() { let generics = generics(self.db.upcast(), def); generics.provenance_split() } else { - (0, 0, 0, 0, 0) + (0, 0, 0, 0, 0, 0) }; TyKind::BoundVar(BoundVar::new( self.in_binders, @@ -808,9 +826,16 @@ impl<'a> TyLoweringContext<'a> { return Substitution::empty(Interner); }; let def_generics = generics(self.db.upcast(), def); - let (parent_params, self_params, type_params, const_params, impl_trait_params) = - def_generics.provenance_split(); - let item_len = self_params + type_params + const_params + impl_trait_params; + let ( + parent_params, + self_params, + type_params, + const_params, + impl_trait_params, + lifetime_params, + ) = def_generics.provenance_split(); + let item_len = + self_params + type_params + const_params + impl_trait_params + lifetime_params; let total_len = parent_params + item_len; let ty_error = TyKind::Error.intern(Interner).cast(Interner); @@ -825,7 +850,10 @@ impl<'a> TyLoweringContext<'a> { .take(self_params) { if let Some(id) = def_generic_iter.next() { - assert!(id.is_left()); + assert!(matches!( + id, + GenericParamId::TypeParamId(_) | GenericParamId::LifetimeParamId(_) + )); substs.push(x); } } @@ -858,6 +886,7 @@ impl<'a> TyLoweringContext<'a> { &mut (), |_, type_ref| self.lower_ty(type_ref), |_, const_ref, ty| self.lower_const(const_ref, ty), + |_, lifetime_ref| self.lower_lifetime(lifetime_ref), ) { had_explicit_args = true; substs.push(x); @@ -867,15 +896,45 @@ impl<'a> TyLoweringContext<'a> { } } } + + for arg in generic_args + .args + .iter() + .filter(|arg| matches!(arg, GenericArg::Lifetime(_))) + .take(lifetime_params) + { + // Taking into the fact that def_generic_iter will always have lifetimes at the end + // Should have some test cases tho to test this behaviour more properly + if let Some(id) = def_generic_iter.next() { + if let Some(x) = generic_arg_to_chalk( + self.db, + id, + arg, + &mut (), + |_, type_ref| self.lower_ty(type_ref), + |_, const_ref, ty| self.lower_const(const_ref, ty), + |_, lifetime_ref| self.lower_lifetime(lifetime_ref), + ) { + had_explicit_args = true; + substs.push(x); + } else { + // Never return a None explicitly + never!("Unexpected None by generic_arg_to_chalk"); + } + } + } } else { fill_self_params(); } // These params include those of parent. let remaining_params: SmallVec<[_; 2]> = def_generic_iter - .map(|eid| match eid { - Either::Left(_) => ty_error.clone(), - Either::Right(x) => unknown_const_as_generic(self.db.const_param_ty(x)), + .map(|id| match id { + GenericParamId::ConstParamId(x) => { + unknown_const_as_generic(self.db.const_param_ty(x)) + } + GenericParamId::TypeParamId(_) => ty_error.clone(), + GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner), }) .collect(); assert_eq!(remaining_params.len() + substs.len(), total_len); @@ -1107,8 +1166,12 @@ impl<'a> TyLoweringContext<'a> { binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(), ); if let Some(type_ref) = &binding.type_ref { - if let (TypeRef::ImplTrait(bounds), ImplTraitLoweringState::Disallowed) = - (type_ref, &self.impl_trait_mode) + if let ( + TypeRef::ImplTrait(bounds), + ImplTraitLoweringState::Param(_) + | ImplTraitLoweringState::Variable(_) + | ImplTraitLoweringState::Disallowed, + ) = (type_ref, &self.impl_trait_mode) { for bound in bounds { predicates.extend( @@ -1270,11 +1333,7 @@ impl<'a> TyLoweringContext<'a> { } } - fn lower_impl_trait( - &self, - bounds: &[Interned], - func: FunctionId, - ) -> ReturnTypeImplTrait { + fn lower_impl_trait(&self, bounds: &[Interned], krate: CrateId) -> ImplTrait { cov_mark::hit!(lower_rpit); let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner); let predicates = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { @@ -1284,7 +1343,6 @@ impl<'a> TyLoweringContext<'a> { .collect(); if !ctx.unsized_types.borrow().contains(&self_ty) { - let krate = func.krate(ctx.db.upcast()); let sized_trait = ctx .db .lang_item(krate, LangItem::Sized) @@ -1301,7 +1359,34 @@ impl<'a> TyLoweringContext<'a> { } predicates }); - ReturnTypeImplTrait { bounds: crate::make_single_type_binders(predicates) } + ImplTrait { bounds: crate::make_single_type_binders(predicates) } + } + + pub fn lower_lifetime(&self, lifetime: &LifetimeRef) -> Lifetime { + match self.resolver.resolve_lifetime(lifetime) { + Some(resolution) => match resolution { + LifetimeNs::Static => static_lifetime(), + LifetimeNs::LifetimeParam(id) => match self.type_param_mode { + ParamLoweringMode::Placeholder => { + LifetimeData::Placeholder(lt_to_placeholder_idx(self.db, id)) + } + ParamLoweringMode::Variable => { + let generics = generics( + self.db.upcast(), + self.resolver.generic_def().expect("generics in scope"), + ); + let idx = match generics.lifetime_idx(id) { + None => return error_lifetime(), + Some(idx) => idx, + }; + + LifetimeData::BoundVar(BoundVar::new(self.in_binders, idx)) + } + } + .intern(Interner), + }, + None => error_lifetime(), + } } } @@ -1685,7 +1770,7 @@ pub(crate) fn generic_defaults_query( let defaults = Arc::from_iter(generic_params.iter().enumerate().map(|(idx, (id, p))| { match p { - TypeOrConstParamData::TypeParamData(p) => { + GenericParamDataRef::TypeParamData(p) => { let mut ty = p.default.as_ref().map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t)); // Each default can only refer to previous parameters. @@ -1694,13 +1779,13 @@ pub(crate) fn generic_defaults_query( ty = fallback_bound_vars(ty, idx, parent_start_idx); crate::make_binders(db, &generic_params, ty.cast(Interner)) } - TypeOrConstParamData::ConstParamData(p) => { + GenericParamDataRef::ConstParamData(p) => { + let GenericParamId::ConstParamId(id) = id else { + unreachable!("Unexpected lifetime or type argument") + }; + let mut val = p.default.as_ref().map_or_else( - || { - unknown_const_as_generic( - db.const_param_ty(ConstParamId::from_unchecked(id)), - ) - }, + || unknown_const_as_generic(db.const_param_ty(id)), |c| { let c = ctx.lower_const(c, ctx.lower_ty(&p.ty)); c.cast(Interner) @@ -1710,6 +1795,10 @@ pub(crate) fn generic_defaults_query( val = fallback_bound_vars(val, idx, parent_start_idx); make_binders(db, &generic_params, val) } + GenericParamDataRef::LifetimeParamData(_) => { + // using static because it requires defaults + make_binders(db, &generic_params, static_lifetime().cast(Interner)) + } } })); @@ -1726,8 +1815,9 @@ pub(crate) fn generic_defaults_recover( // we still need one default per parameter let defaults = Arc::from_iter(generic_params.iter_id().map(|id| { let val = match id { - Either::Left(_) => TyKind::Error.intern(Interner).cast(Interner), - Either::Right(id) => unknown_const_as_generic(db.const_param_ty(id)), + GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner), + GenericParamId::ConstParamId(id) => unknown_const_as_generic(db.const_param_ty(id)), + GenericParamId::LifetimeParamId(_) => static_lifetime().cast(Interner), }; crate::make_binders(db, &generic_params, val) })); @@ -1869,6 +1959,7 @@ fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders { let generics = generics(db.upcast(), t.into()); let resolver = t.resolver(db.upcast()); let ctx = TyLoweringContext::new(db, &resolver, t.into()) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) .with_type_param_mode(ParamLoweringMode::Variable); let type_alias_data = db.type_alias_data(t); if type_alias_data.is_extern { @@ -2029,7 +2120,7 @@ pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option< pub(crate) fn return_type_impl_traits( db: &dyn HirDatabase, def: hir_def::FunctionId, -) -> Option>> { +) -> Option>> { // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe let data = db.function_data(def); let resolver = def.resolver(db.upcast()); @@ -2038,7 +2129,7 @@ pub(crate) fn return_type_impl_traits( .with_type_param_mode(ParamLoweringMode::Variable); let _ret = ctx_ret.lower_ty(&data.ret_type); let generics = generics(db.upcast(), def.into()); - let return_type_impl_traits = ReturnTypeImplTraits { + let return_type_impl_traits = ImplTraits { impl_traits: match ctx_ret.impl_trait_mode { ImplTraitLoweringState::Opaque(x) => x.into_inner(), _ => unreachable!(), @@ -2051,6 +2142,32 @@ pub(crate) fn return_type_impl_traits( } } +pub(crate) fn type_alias_impl_traits( + db: &dyn HirDatabase, + def: hir_def::TypeAliasId, +) -> Option>> { + let data = db.type_alias_data(def); + let resolver = def.resolver(db.upcast()); + let ctx = TyLoweringContext::new(db, &resolver, def.into()) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) + .with_type_param_mode(ParamLoweringMode::Variable); + if let Some(type_ref) = &data.type_ref { + let _ty = ctx.lower_ty(type_ref); + } + let generics = generics(db.upcast(), def.into()); + let type_alias_impl_traits = ImplTraits { + impl_traits: match ctx.impl_trait_mode { + ImplTraitLoweringState::Opaque(x) => x.into_inner(), + _ => unreachable!(), + }, + }; + if type_alias_impl_traits.impl_traits.is_empty() { + None + } else { + Some(Arc::new(make_binders(db, &generics, type_alias_impl_traits))) + } +} + pub(crate) fn lower_to_chalk_mutability(m: hir_def::type_ref::Mutability) -> Mutability { match m { hir_def::type_ref::Mutability::Shared => Mutability::Not, @@ -2064,23 +2181,29 @@ pub(crate) fn lower_to_chalk_mutability(m: hir_def::type_ref::Mutability) -> Mut /// Returns `Some` of the lowered generic arg. `None` if the provided arg is a lifetime. pub(crate) fn generic_arg_to_chalk<'a, T>( db: &dyn HirDatabase, - kind_id: Either, + kind_id: GenericParamId, arg: &'a GenericArg, this: &mut T, for_type: impl FnOnce(&mut T, &TypeRef) -> Ty + 'a, for_const: impl FnOnce(&mut T, &ConstRef, Ty) -> Const + 'a, + for_lifetime: impl FnOnce(&mut T, &LifetimeRef) -> Lifetime + 'a, ) -> Option { let kind = match kind_id { - Either::Left(_) => ParamKind::Type, - Either::Right(id) => { + GenericParamId::TypeParamId(_) => ParamKind::Type, + GenericParamId::ConstParamId(id) => { let ty = db.const_param_ty(id); ParamKind::Const(ty) } + GenericParamId::LifetimeParamId(_) => ParamKind::Lifetime, }; Some(match (arg, kind) { (GenericArg::Type(type_ref), ParamKind::Type) => for_type(this, type_ref).cast(Interner), (GenericArg::Const(c), ParamKind::Const(c_ty)) => for_const(this, c, c_ty).cast(Interner), + (GenericArg::Lifetime(lifetime_ref), ParamKind::Lifetime) => { + for_lifetime(this, lifetime_ref).cast(Interner) + } (GenericArg::Const(_), ParamKind::Type) => TyKind::Error.intern(Interner).cast(Interner), + (GenericArg::Lifetime(_), ParamKind::Type) => TyKind::Error.intern(Interner).cast(Interner), (GenericArg::Type(t), ParamKind::Const(c_ty)) => { // We want to recover simple idents, which parser detects them // as types. Maybe here is not the best place to do it, but @@ -2096,7 +2219,9 @@ pub(crate) fn generic_arg_to_chalk<'a, T>( } unknown_const_as_generic(c_ty) } - (GenericArg::Lifetime(_), _) => return None, + (GenericArg::Lifetime(_), ParamKind::Const(c_ty)) => unknown_const_as_generic(c_ty), + (GenericArg::Type(_), ParamKind::Lifetime) => error_lifetime().cast(Interner), + (GenericArg::Const(_), ParamKind::Lifetime) => error_lifetime().cast(Interner), }) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs index fba760974f24b..c61d82771429a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs @@ -151,6 +151,14 @@ pub fn lt_from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> L db.lookup_intern_lifetime_param_id(interned_id) } +pub fn lt_to_placeholder_idx(db: &dyn HirDatabase, id: LifetimeParamId) -> PlaceholderIndex { + let interned_id = db.intern_lifetime_param_id(id); + PlaceholderIndex { + ui: chalk_ir::UniverseIndex::ROOT, + idx: salsa::InternKey::as_intern_id(&interned_id).as_usize(), + } +} + pub fn to_chalk_trait_id(id: TraitId) -> ChalkTraitId { chalk_ir::TraitId(salsa::InternKey::as_intern_id(&id)) } 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 a679a114b4b93..73b07df56f7ea 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 @@ -4,7 +4,7 @@ //! and the corresponding code mostly in rustc_hir_analysis/check/method/probe.rs. use std::ops::ControlFlow; -use base_db::{CrateId, Edition}; +use base_db::CrateId; use chalk_ir::{cast::Cast, Mutability, TyKind, UniverseIndex, WhereClause}; use hir_def::{ data::{adt::StructFlags, ImplData}, @@ -15,6 +15,7 @@ use hir_def::{ use hir_expand::name::Name; use rustc_hash::{FxHashMap, FxHashSet}; use smallvec::{smallvec, SmallVec}; +use span::Edition; use stdx::never; use triomphe::Arc; @@ -643,7 +644,7 @@ pub fn is_dyn_method( let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else { return None; }; - let trait_params = db.generic_params(trait_id.into()).type_or_consts.len(); + let trait_params = db.generic_params(trait_id.into()).len(); let fn_params = fn_subst.len(Interner) - trait_params; let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), @@ -685,7 +686,7 @@ pub(crate) fn lookup_impl_method_query( let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else { return (func, fn_subst); }; - let trait_params = db.generic_params(trait_id.into()).type_or_consts.len(); + let trait_params = db.generic_params(trait_id.into()).len(); let fn_params = fn_subst.len(Interner) - trait_params; let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), @@ -966,7 +967,7 @@ pub fn iterate_method_candidates_dyn( // the methods by autoderef order of *receiver types*, not *self // types*. - let mut table = InferenceTable::new(db, env.clone()); + let mut table = InferenceTable::new(db, env); let ty = table.instantiate_canonical(ty.clone()); let deref_chain = autoderef_method_receiver(&mut table, ty); @@ -1044,7 +1045,7 @@ fn iterate_method_candidates_with_autoref( let ref_muted = Canonical { value: TyKind::Ref(Mutability::Mut, static_lifetime(), receiver_ty.value.clone()) .intern(Interner), - binders: receiver_ty.binders.clone(), + binders: receiver_ty.binders, }; iterate_method_candidates_by_receiver(ref_muted, first_adjustment.with_autoref(Mutability::Mut)) @@ -1060,7 +1061,7 @@ fn iterate_method_candidates_by_receiver( name: Option<&Name>, mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, ) -> ControlFlow<()> { - let receiver_ty = table.instantiate_canonical(receiver_ty.clone()); + let receiver_ty = table.instantiate_canonical(receiver_ty); // We're looking for methods with *receiver* type receiver_ty. These could // be found in any of the derefs of receiver_ty, so we have to go through // that, including raw derefs. @@ -1456,7 +1457,7 @@ fn is_valid_trait_method_candidate( if let Some(receiver_ty) = receiver_ty { check_that!(data.has_self_param()); - let fn_subst = TyBuilder::subst_for_def(db, fn_id, Some(impl_subst.clone())) + let fn_subst = TyBuilder::subst_for_def(db, fn_id, Some(impl_subst)) .fill_with_inference_vars(table) .build(); 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 fd98141af63e6..045ffb418c8de 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 @@ -1931,7 +1931,11 @@ impl Evaluator<'_> { ty: &Ty, locals: &Locals, mm: &mut ComplexMemoryMap, + stack_depth_limit: usize, ) -> Result<()> { + if stack_depth_limit.checked_sub(1).is_none() { + return Err(MirEvalError::StackOverflow); + } match ty.kind(Interner) { TyKind::Ref(_, _, t) => { let size = this.size_align_of(t, locals)?; @@ -1970,7 +1974,14 @@ impl Evaluator<'_> { if let Some(ty) = check_inner { for i in 0..count { let offset = element_size * i; - rec(this, &b[offset..offset + element_size], ty, locals, mm)?; + rec( + this, + &b[offset..offset + element_size], + ty, + locals, + mm, + stack_depth_limit - 1, + )?; } } } @@ -1984,7 +1995,14 @@ impl Evaluator<'_> { let size = this.size_of_sized(inner, locals, "inner of array")?; for i in 0..len { let offset = i * size; - rec(this, &bytes[offset..offset + size], inner, locals, mm)?; + rec( + this, + &bytes[offset..offset + size], + inner, + locals, + mm, + stack_depth_limit - 1, + )?; } } chalk_ir::TyKind::Tuple(_, subst) => { @@ -1993,7 +2011,14 @@ impl Evaluator<'_> { let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument let offset = layout.fields.offset(id).bytes_usize(); let size = this.layout(ty)?.size.bytes_usize(); - rec(this, &bytes[offset..offset + size], ty, locals, mm)?; + rec( + this, + &bytes[offset..offset + size], + ty, + locals, + mm, + stack_depth_limit - 1, + )?; } } chalk_ir::TyKind::Adt(adt, subst) => match adt.0 { @@ -2008,7 +2033,14 @@ impl Evaluator<'_> { .bytes_usize(); let ty = &field_types[f].clone().substitute(Interner, subst); let size = this.layout(ty)?.size.bytes_usize(); - rec(this, &bytes[offset..offset + size], ty, locals, mm)?; + rec( + this, + &bytes[offset..offset + size], + ty, + locals, + mm, + stack_depth_limit - 1, + )?; } } AdtId::EnumId(e) => { @@ -2027,7 +2059,14 @@ impl Evaluator<'_> { l.fields.offset(u32::from(f.into_raw()) as usize).bytes_usize(); let ty = &field_types[f].clone().substitute(Interner, subst); let size = this.layout(ty)?.size.bytes_usize(); - rec(this, &bytes[offset..offset + size], ty, locals, mm)?; + rec( + this, + &bytes[offset..offset + size], + ty, + locals, + mm, + stack_depth_limit - 1, + )?; } } } @@ -2038,7 +2077,7 @@ impl Evaluator<'_> { Ok(()) } let mut mm = ComplexMemoryMap::default(); - rec(self, bytes, ty, locals, &mut mm)?; + rec(self, bytes, ty, locals, &mut mm, self.stack_depth_limit - 1)?; Ok(mm) } @@ -2317,7 +2356,7 @@ impl Evaluator<'_> { fn exec_fn_with_args( &mut self, - def: FunctionId, + mut def: FunctionId, args: &[IntervalAndTy], generic_args: Substitution, locals: &Locals, @@ -2335,6 +2374,9 @@ impl Evaluator<'_> { )? { return Ok(None); } + if let Some(redirect_def) = self.detect_and_redirect_special_function(def)? { + def = redirect_def; + } let arg_bytes = args.iter().map(|it| IntervalOrOwned::Borrowed(it.interval)); match self.get_mir_or_dyn_index(def, generic_args.clone(), locals, span)? { MirOrDynIndex::Dyn(self_ty_idx) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index 628a1fe2d2838..d4d669182f2e3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -13,7 +13,7 @@ use crate::mir::eval::{ name, pad16, static_lifetime, Address, AdtId, Arc, BuiltinType, Evaluator, FunctionId, HasModule, HirDisplay, Interned, InternedClosure, Interner, Interval, IntervalAndTy, IntervalOrOwned, ItemContainerId, LangItem, Layout, Locals, Lookup, MirEvalError, MirSpan, - ModPath, Mutability, Result, Substitution, Ty, TyBuilder, TyExt, + Mutability, Result, Substitution, Ty, TyBuilder, TyExt, }; mod simd; @@ -158,6 +158,25 @@ impl Evaluator<'_> { Ok(false) } + pub(super) fn detect_and_redirect_special_function( + &mut self, + def: FunctionId, + ) -> Result> { + // `PanicFmt` is redirected to `ConstPanicFmt` + if let Some(LangItem::PanicFmt) = self.db.lang_attr(def.into()) { + let resolver = + self.db.crate_def_map(self.crate_id).crate_root().resolver(self.db.upcast()); + + let Some(hir_def::lang_item::LangItemTarget::Function(const_panic_fmt)) = + self.db.lang_item(resolver.krate(), LangItem::ConstPanicFmt) + else { + not_supported!("const_panic_fmt lang item not found or not a function"); + }; + return Ok(Some(const_panic_fmt)); + } + Ok(None) + } + /// Clone has special impls for tuples and function pointers fn exec_clone( &mut self, @@ -291,9 +310,14 @@ impl Evaluator<'_> { use LangItem::*; let candidate = self.db.lang_attr(def.into())?; // We want to execute these functions with special logic - if [PanicFmt, BeginPanic, SliceLen, DropInPlace].contains(&candidate) { + // `PanicFmt` is not detected here as it's redirected later. + if [BeginPanic, SliceLen, DropInPlace].contains(&candidate) { return Some(candidate); } + if self.db.attrs(def.into()).by_key("rustc_const_panic_str").exists() { + // `#[rustc_const_panic_str]` is treated like `lang = "begin_panic"` by rustc CTFE. + return Some(LangItem::BeginPanic); + } None } @@ -309,43 +333,6 @@ impl Evaluator<'_> { let mut args = args.iter(); match it { BeginPanic => Err(MirEvalError::Panic("".to_owned())), - PanicFmt => { - let message = (|| { - let resolver = self - .db - .crate_def_map(self.crate_id) - .crate_root() - .resolver(self.db.upcast()); - let Some(format_fn) = resolver.resolve_path_in_value_ns_fully( - self.db.upcast(), - &hir_def::path::Path::from_known_path_with_no_generic( - ModPath::from_segments( - hir_expand::mod_path::PathKind::Abs, - [name![std], name![fmt], name![format]], - ), - ), - ) else { - not_supported!("std::fmt::format not found"); - }; - let hir_def::resolver::ValueNs::FunctionId(format_fn) = format_fn else { - not_supported!("std::fmt::format is not a function") - }; - let interval = self.interpret_mir( - self.db - .mir_body(format_fn.into()) - .map_err(|e| MirEvalError::MirLowerError(format_fn, e))?, - args.map(|x| IntervalOrOwned::Owned(x.clone())), - )?; - let message_string = interval.get(self)?; - let addr = - Address::from_bytes(&message_string[self.ptr_size()..2 * self.ptr_size()])?; - let size = from_bytes!(usize, message_string[2 * self.ptr_size()..]); - Ok(std::string::String::from_utf8_lossy(self.read_memory(addr, size)?) - .into_owned()) - })() - .unwrap_or_else(|e| format!("Failed to render panic format args: {e:?}")); - Err(MirEvalError::Panic(message)) - } SliceLen => { let arg = args.next().ok_or(MirEvalError::InternalError( "argument of <[T]>::len() is not provided".into(), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs index d2e413f0a33a4..d6557c3a8160a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs @@ -82,6 +82,9 @@ impl FallibleTypeFolder for Filler<'_> { }; filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder) } + crate::ImplTraitId::AssociatedTypeImplTrait(..) => { + not_supported!("associated type impl trait"); + } crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { not_supported!("async block impl trait"); } @@ -181,8 +184,16 @@ impl Filler<'_> { self.generics .as_ref() .and_then(|it| it.iter().nth(b.index)) - .unwrap() - .0, + .and_then(|(id, _)| match id { + hir_def::GenericParamId::ConstParamId(id) => { + Some(hir_def::TypeOrConstParamId::from(id)) + } + hir_def::GenericParamId::TypeParamId(id) => { + Some(hir_def::TypeOrConstParamId::from(id)) + } + _ => None, + }) + .unwrap(), self.subst.clone(), ) })? 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 d699067b5a645..2a46becbfda95 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -298,7 +298,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { if let Some(syntax_ptr) = body_source_map.self_param_syntax() { let root = db.parse_or_expand(syntax_ptr.file_id); let node = syntax_ptr.map(|ptr| ptr.to_node(&root).syntax().clone()); - types.push((node.clone(), ty)); + types.push((node, ty)); } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs index e75b037e38d3e..506926749960e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs @@ -85,7 +85,7 @@ fn render_dyn_for_ty() { trait Foo<'a> {} fn foo(foo: &dyn for<'a> Foo<'a>) {} - // ^^^ &dyn Foo + // ^^^ &dyn Foo<'static> "#, ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs index 963b4a2aba05f..80d5a0ae001b0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs @@ -1109,7 +1109,7 @@ fn var_args() { #[lang = "va_list"] pub struct VaListImpl<'f>; fn my_fn(foo: ...) {} - //^^^ VaListImpl<'_> + //^^^ VaListImpl<'static> "#, ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 9a8ebd07d0159..8565b60210ba7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -896,13 +896,13 @@ fn flush(&self) { "#, expect![[r#" 123..127 'self': &Mutex - 150..152 '{}': MutexGuard<'_, T> + 150..152 '{}': MutexGuard<'static, T> 234..238 'self': &{unknown} 240..290 '{ ...()); }': () 250..251 'w': &Mutex 276..287 '*(w.lock())': BufWriter 278..279 'w': &Mutex - 278..286 'w.lock()': MutexGuard<'_, BufWriter> + 278..286 'w.lock()': MutexGuard<'static, BufWriter> "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index 917e9f440852d..f39404593e5d8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -3092,7 +3092,7 @@ fn main() { 389..394 'boxed': Box> 389..406 'boxed....nner()': &i32 416..421 'good1': &i32 - 424..438 'Foo::get_inner': fn get_inner(&Box>) -> &i32 + 424..438 'Foo::get_inner': fn get_inner(&Box>) -> &i32 424..446 'Foo::g...boxed)': &i32 439..445 '&boxed': &Box> 440..445 'boxed': Box> @@ -3100,7 +3100,7 @@ fn main() { 464..469 'boxed': Box> 464..480 'boxed....self()': &Foo 490..495 'good2': &Foo - 498..511 'Foo::get_self': fn get_self(&Box>) -> &Foo + 498..511 'Foo::get_self': fn get_self(&Box>) -> &Foo 498..519 'Foo::g...boxed)': &Foo 512..518 '&boxed': &Box> 513..518 'boxed': Box> @@ -3659,7 +3659,7 @@ fn main() { let are = "are"; let count = 10; builtin#format_args("hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!"); - // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type: Arguments<'_> + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type: Arguments<'static> } "#, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index b80cfe18e4cf9..759af18c98bf5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -1278,6 +1278,40 @@ fn bar() { ); } +#[test] +fn argument_assoc_impl_trait() { + check_infer( + r#" +trait Outer { + type Item; +} + +trait Inner { } + +fn foo>(baz: T) { +} + +impl Outer for usize { + type Item = usize; +} + +impl Inner for usize {} + +fn main() { + foo(2); +} +"#, + expect![[r#" + 85..88 'baz': T + 93..96 '{ }': () + 182..197 '{ foo(2); }': () + 188..191 'foo': fn foo(usize) + 188..194 'foo(2)': () + 192..193 '2': usize + "#]], + ); +} + #[test] fn simple_return_pos_impl_trait() { cov_mark::check!(lower_rpit); @@ -4655,3 +4689,78 @@ fn f() { "#, ); } + +#[test] +fn associated_type_impl_trait() { + check_types( + r#" +trait Foo {} +struct S1; +impl Foo for S1 {} + +trait Bar { + type Item; + fn bar(&self) -> Self::Item; +} +struct S2; +impl Bar for S2 { + type Item = impl Foo; + fn bar(&self) -> Self::Item { + S1 + } +} + +fn test() { + let x = S2.bar(); + //^ impl Foo + ?Sized +} + "#, + ); +} + +#[test] +fn associated_type_impl_traits_complex() { + check_types( + r#" +struct Unary(T); +struct Binary(T, U); + +trait Foo {} +struct S1; +impl Foo for S1 {} + +trait Bar { + type Item; + fn bar(&self) -> Unary; +} +struct S2; +impl Bar for S2 { + type Item = Unary; + fn bar(&self) -> Unary<::Item> { + Unary(Unary(S1)) + } +} + +trait Baz { + type Target1; + type Target2; + fn baz(&self) -> Binary; +} +struct S3; +impl Baz for S3 { + type Target1 = impl Foo; + type Target2 = Unary; + fn baz(&self) -> Binary { + Binary(S1, Unary(S2)) + } +} + +fn test() { + let x = S3.baz(); + //^ Binary> + let y = x.1.0.bar(); + //^ Unary> +} + "#, + ); +} 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 8bd57820d2cdb..afd4d1f271df2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs @@ -9,18 +9,18 @@ use chalk_ir::{ fold::{FallibleTypeFolder, Shift}, BoundVar, DebruijnIndex, }; -use either::Either; use hir_def::{ db::DefDatabase, generics::{ - GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate, - WherePredicateTypeTarget, + GenericParamDataRef, GenericParams, LifetimeParamData, TypeOrConstParamData, + TypeParamProvenance, WherePredicate, WherePredicateTypeTarget, }, lang_item::LangItem, resolver::{HasResolver, TypeNs}, type_ref::{TraitBoundModifier, TypeRef}, - ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, ItemContainerId, Lookup, - OpaqueInternableThing, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, + ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, GenericParamId, ItemContainerId, + LifetimeParamId, Lookup, OpaqueInternableThing, TraitId, TypeAliasId, TypeOrConstParamId, + TypeParamId, }; use hir_expand::name::Name; use intern::Interned; @@ -270,64 +270,130 @@ pub(crate) struct Generics { } impl Generics { - pub(crate) fn iter_id(&self) -> impl Iterator> + '_ { - self.iter().map(|(id, data)| match data { - TypeOrConstParamData::TypeParamData(_) => Either::Left(TypeParamId::from_unchecked(id)), - TypeOrConstParamData::ConstParamData(_) => { - Either::Right(ConstParamId::from_unchecked(id)) - } - }) + pub(crate) fn iter_id(&self) -> impl Iterator + '_ { + self.iter().map(|(id, _)| id) } /// Iterator over types and const params of self, then parent. pub(crate) fn iter<'a>( &'a self, - ) -> impl DoubleEndedIterator + 'a { - let to_toc_id = |it: &'a Generics| { - move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p) + ) -> impl DoubleEndedIterator)> + 'a { + let from_toc_id = |it: &'a Generics| { + move |(local_id, p): (_, &'a TypeOrConstParamData)| { + let id = TypeOrConstParamId { parent: it.def, local_id }; + match p { + TypeOrConstParamData::TypeParamData(p) => ( + GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)), + GenericParamDataRef::TypeParamData(p), + ), + TypeOrConstParamData::ConstParamData(p) => ( + GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)), + GenericParamDataRef::ConstParamData(p), + ), + } + } }; - self.params.iter().map(to_toc_id(self)).chain(self.iter_parent()) + + let from_lt_id = |it: &'a Generics| { + move |(local_id, p): (_, &'a LifetimeParamData)| { + ( + GenericParamId::LifetimeParamId(LifetimeParamId { parent: it.def, local_id }), + GenericParamDataRef::LifetimeParamData(p), + ) + } + }; + + let lt_iter = self.params.iter_lt().map(from_lt_id(self)); + self.params.iter().map(from_toc_id(self)).chain(lt_iter).chain(self.iter_parent()) } /// Iterate over types and const params without parent params. pub(crate) fn iter_self<'a>( &'a self, - ) -> impl DoubleEndedIterator + 'a { - let to_toc_id = |it: &'a Generics| { - move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p) + ) -> impl DoubleEndedIterator)> + 'a { + let from_toc_id = |it: &'a Generics| { + move |(local_id, p): (_, &'a TypeOrConstParamData)| { + let id = TypeOrConstParamId { parent: it.def, local_id }; + match p { + TypeOrConstParamData::TypeParamData(p) => ( + GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)), + GenericParamDataRef::TypeParamData(p), + ), + TypeOrConstParamData::ConstParamData(p) => ( + GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)), + GenericParamDataRef::ConstParamData(p), + ), + } + } + }; + + let from_lt_id = |it: &'a Generics| { + move |(local_id, p): (_, &'a LifetimeParamData)| { + ( + GenericParamId::LifetimeParamId(LifetimeParamId { parent: it.def, local_id }), + GenericParamDataRef::LifetimeParamData(p), + ) + } }; - self.params.iter().map(to_toc_id(self)) + + self.params.iter().map(from_toc_id(self)).chain(self.params.iter_lt().map(from_lt_id(self))) } /// Iterator over types and const params of parent. - pub(crate) fn iter_parent( - &self, - ) -> impl DoubleEndedIterator { + #[allow(clippy::needless_lifetimes)] + pub(crate) fn iter_parent<'a>( + &'a self, + ) -> impl DoubleEndedIterator)> + 'a { self.parent_generics().into_iter().flat_map(|it| { - let to_toc_id = - move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p); - it.params.iter().map(to_toc_id) + let from_toc_id = move |(local_id, p): (_, &'a TypeOrConstParamData)| { + let id = TypeOrConstParamId { parent: it.def, local_id }; + match p { + TypeOrConstParamData::TypeParamData(p) => ( + GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)), + GenericParamDataRef::TypeParamData(p), + ), + TypeOrConstParamData::ConstParamData(p) => ( + GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)), + GenericParamDataRef::ConstParamData(p), + ), + } + }; + + let from_lt_id = move |(local_id, p): (_, &'a LifetimeParamData)| { + ( + GenericParamId::LifetimeParamId(LifetimeParamId { parent: it.def, local_id }), + GenericParamDataRef::LifetimeParamData(p), + ) + }; + let lt_iter = it.params.iter_lt().map(from_lt_id); + it.params.iter().map(from_toc_id).chain(lt_iter) }) } /// Returns total number of generic parameters in scope, including those from parent. pub(crate) fn len(&self) -> usize { let parent = self.parent_generics().map_or(0, Generics::len); - let child = self.params.type_or_consts.len(); + let child = self.params.len(); parent + child } - /// Returns numbers of generic parameters excluding those from parent. + /// Returns numbers of generic parameters and lifetimes excluding those from parent. pub(crate) fn len_self(&self) -> usize { + self.params.len() + } + + /// Returns number of generic parameter excluding those from parent + fn len_params(&self) -> usize { self.params.type_or_consts.len() } - /// (parent total, self param, type param list, const param list, impl trait) - pub(crate) fn provenance_split(&self) -> (usize, usize, usize, usize, usize) { + /// (parent total, self param, type params, const params, impl trait list, lifetimes) + pub(crate) fn provenance_split(&self) -> (usize, usize, usize, usize, usize, usize) { let mut self_params = 0; let mut type_params = 0; let mut impl_trait_params = 0; let mut const_params = 0; + let mut lifetime_params = 0; self.params.iter().for_each(|(_, data)| match data { TypeOrConstParamData::TypeParamData(p) => match p.provenance { TypeParamProvenance::TypeParamList => type_params += 1, @@ -337,8 +403,10 @@ impl Generics { TypeOrConstParamData::ConstParamData(_) => const_params += 1, }); + self.params.iter_lt().for_each(|(_, _)| lifetime_params += 1); + let parent_len = self.parent_generics().map_or(0, Generics::len); - (parent_len, self_params, type_params, const_params, impl_trait_params) + (parent_len, self_params, type_params, const_params, impl_trait_params, lifetime_params) } pub(crate) fn param_idx(&self, param: TypeOrConstParamId) -> Option { @@ -358,6 +426,26 @@ impl Generics { } } + pub(crate) fn lifetime_idx(&self, lifetime: LifetimeParamId) -> Option { + Some(self.find_lifetime(lifetime)?.0) + } + + fn find_lifetime(&self, lifetime: LifetimeParamId) -> Option<(usize, &LifetimeParamData)> { + if lifetime.parent == self.def { + let (idx, (_local_id, data)) = self + .params + .iter_lt() + .enumerate() + .find(|(_, (idx, _))| *idx == lifetime.local_id)?; + + Some((self.len_params() + idx, data)) + } else { + self.parent_generics() + .and_then(|g| g.find_lifetime(lifetime)) + .map(|(idx, data)| (self.len_self() + idx, data)) + } + } + pub(crate) fn parent_generics(&self) -> Option<&Generics> { self.parent_generics.as_deref() } @@ -371,10 +459,15 @@ impl Generics { Substitution::from_iter( Interner, self.iter_id().enumerate().map(|(idx, id)| match id { - Either::Left(_) => BoundVar::new(debruijn, idx).to_ty(Interner).cast(Interner), - Either::Right(id) => BoundVar::new(debruijn, idx) + GenericParamId::ConstParamId(id) => BoundVar::new(debruijn, idx) .to_const(Interner, db.const_param_ty(id)) .cast(Interner), + GenericParamId::TypeParamId(_) => { + BoundVar::new(debruijn, idx).to_ty(Interner).cast(Interner) + } + GenericParamId::LifetimeParamId(_) => { + BoundVar::new(debruijn, idx).to_lifetime(Interner).cast(Interner) + } }), ) } @@ -384,12 +477,15 @@ impl Generics { Substitution::from_iter( Interner, self.iter_id().map(|id| match id { - Either::Left(id) => { + GenericParamId::TypeParamId(id) => { crate::to_placeholder_idx(db, id.into()).to_ty(Interner).cast(Interner) } - Either::Right(id) => crate::to_placeholder_idx(db, id.into()) + GenericParamId::ConstParamId(id) => crate::to_placeholder_idx(db, id.into()) .to_const(Interner, db.const_param_ty(id)) .cast(Interner), + GenericParamId::LifetimeParamId(id) => { + crate::lt_to_placeholder_idx(db, id).to_lifetime(Interner).cast(Interner) + } }), ) } diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index c5d44c11f2c1b..23c6b078b9696 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -186,18 +186,29 @@ impl HirDisplay for Struct { } StructKind::Record => { let has_where_clause = write_where_clause(def_id, f)?; - let fields = self.fields(f.db); - f.write_char(if !has_where_clause { ' ' } else { '\n' })?; - if fields.is_empty() { - f.write_str("{}")?; - } else { - f.write_str("{\n")?; - for field in self.fields(f.db) { - f.write_str(" ")?; - field.hir_fmt(f)?; - f.write_str(",\n")?; + if let Some(limit) = f.entity_limit { + let fields = self.fields(f.db); + let count = fields.len().min(limit); + f.write_char(if !has_where_clause { ' ' } else { '\n' })?; + if count == 0 { + if fields.is_empty() { + f.write_str("{}")?; + } else { + f.write_str("{ /* … */ }")?; + } + } else { + f.write_str(" {\n")?; + for field in &fields[..count] { + f.write_str(" ")?; + field.hir_fmt(f)?; + f.write_str(",\n")?; + } + + if fields.len() > count { + f.write_str(" /* … */\n")?; + } + f.write_str("}")?; } - f.write_str("}")?; } } StructKind::Unit => _ = write_where_clause(def_id, f)?, diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index b922aa8e46dbe..106056c2fc3a9 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -38,7 +38,7 @@ mod display; use std::{iter, mem::discriminant, ops::ControlFlow}; use arrayvec::ArrayVec; -use base_db::{CrateDisplayName, CrateId, CrateOrigin, Edition, FileId}; +use base_db::{CrateDisplayName, CrateId, CrateOrigin, FileId}; use either::Either; use hir_def::{ body::{BodyDiagnostic, SyntheticSyntax}, @@ -65,7 +65,7 @@ use hir_ty::{ consteval::{try_const_usize, unknown_const_as_generic, ConstExt}, db::InternedClosure, diagnostics::BodyValidationDiagnostic, - known_const_to_ast, + error_lifetime, known_const_to_ast, layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding}, method_resolution::{self, TyFingerprint}, mir::{interpret_mir, MutBorrowKind}, @@ -79,6 +79,7 @@ use hir_ty::{ use itertools::Itertools; use nameres::diagnostics::DefDiagnosticKind; use rustc_hash::FxHashSet; +use span::Edition; use stdx::{impl_from, never}; use syntax::{ ast::{self, HasAttrs as _, HasName}, @@ -971,7 +972,7 @@ fn precise_macro_call_location( MacroKind::ProcMacro, ) } - MacroCallKind::Derive { ast_id, derive_attr_index, derive_index } => { + MacroCallKind::Derive { ast_id, derive_attr_index, derive_index, .. } => { let node = ast_id.to_node(db.upcast()); // Compute the precise location of the macro name's token in the derive // list. @@ -1099,13 +1100,14 @@ impl Field { VariantDef::Union(it) => it.id.into(), VariantDef::Variant(it) => it.parent_enum(db).id.into(), }; - let mut generics = generics.map(|it| it.ty.clone()); + let mut generics = generics.map(|it| it.ty); let substs = TyBuilder::subst_for_def(db, def_id, None) .fill(|x| match x { ParamKind::Type => { generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)).cast(Interner) } ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), + ParamKind::Lifetime => error_lifetime().cast(Interner), }) .build(); let ty = db.field_types(var_id)[self.id].clone().substitute(Interner, &substs); @@ -1416,7 +1418,7 @@ impl Adt { } pub fn layout(self, db: &dyn HirDatabase) -> Result { - if db.generic_params(self.into()).iter().count() != 0 { + if !db.generic_params(self.into()).is_empty() { return Err(LayoutError::HasPlaceholder); } let krate = self.krate(db).id; @@ -1440,13 +1442,14 @@ impl Adt { /// the greatest API, FIXME find a better one. pub fn ty_with_args(self, db: &dyn HirDatabase, args: impl Iterator) -> Type { let id = AdtId::from(self); - let mut it = args.map(|t| t.ty.clone()); + let mut it = args.map(|t| t.ty); let ty = TyBuilder::def_ty(db, id.into(), None) .fill(|x| { let r = it.next().unwrap_or_else(|| TyKind::Error.intern(Interner)); match x { ParamKind::Type => r.cast(Interner), ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), + ParamKind::Lifetime => error_lifetime().cast(Interner), } }) .build(); @@ -1859,12 +1862,13 @@ impl Function { ItemContainerId::TraitId(it) => Some(it.into()), ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None, }; - let mut generics = generics.map(|it| it.ty.clone()); + let mut generics = generics.map(|it| it.ty); let mut filler = |x: &_| match x { ParamKind::Type => { generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)).cast(Interner) } ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), + ParamKind::Lifetime => error_lifetime().cast(Interner), }; let parent_substs = @@ -1954,7 +1958,7 @@ impl Function { ItemContainerId::TraitId(it) => Some(it.into()), ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None, }; - let mut generics = generics.map(|it| it.ty.clone()); + let mut generics = generics.map(|it| it.ty); let parent_substs = parent_id.map(|id| { TyBuilder::subst_for_def(db, id, None) .fill(|x| match x { @@ -1963,6 +1967,7 @@ impl Function { .unwrap_or_else(|| TyKind::Error.intern(Interner)) .cast(Interner), ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), + ParamKind::Lifetime => error_lifetime().cast(Interner), }) .build() }); @@ -2007,8 +2012,7 @@ impl Function { } let data = db.function_data(self.id); - data.name.to_smol_str() == "main" - || data.attrs.export_name().map(core::ops::Deref::deref) == Some("main") + data.name.to_smol_str() == "main" || data.attrs.export_name() == Some("main") } /// Does this function have the ignore attribute? @@ -2215,12 +2219,13 @@ impl SelfParam { } }; - let mut generics = generics.map(|it| it.ty.clone()); + let mut generics = generics.map(|it| it.ty); let mut filler = |x: &_| match x { ParamKind::Type => { generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)).cast(Interner) } ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), + ParamKind::Lifetime => error_lifetime().cast(Interner), }; let parent_substs = TyBuilder::subst_for_def(db, parent_id, None).fill(&mut filler).build(); @@ -2592,7 +2597,7 @@ impl Macro { }, MacroId::ProcMacroId(it) => match it.lookup(db.upcast()).kind { ProcMacroKind::CustomDerive => MacroKind::Derive, - ProcMacroKind::FuncLike => MacroKind::ProcMacro, + ProcMacroKind::Bang => MacroKind::ProcMacro, ProcMacroKind::Attr => MacroKind::Attr, }, } @@ -3628,16 +3633,41 @@ impl Impl { .filter(filter), ); } + + if let Some(block) = + ty.adt_id(Interner).and_then(|def| def.0.module(db.upcast()).containing_block()) + { + if let Some(inherent_impls) = db.inherent_impls_in_block(block) { + all.extend( + inherent_impls.for_self_ty(&ty).iter().cloned().map(Self::from).filter(filter), + ); + } + if let Some(trait_impls) = db.trait_impls_in_block(block) { + all.extend( + trait_impls + .for_self_ty_without_blanket_impls(fp) + .map(Self::from) + .filter(filter), + ); + } + } + all } pub fn all_for_trait(db: &dyn HirDatabase, trait_: Trait) -> Vec { - let krate = trait_.module(db).krate(); + let module = trait_.module(db); + let krate = module.krate(); let mut all = Vec::new(); for Crate { id } in krate.transitive_reverse_dependencies(db) { let impls = db.trait_impls_in_crate(id); all.extend(impls.for_trait(trait_.id).map(Self::from)) } + if let Some(block) = module.id.containing_block() { + if let Some(trait_impls) = db.trait_impls_in_block(block) { + all.extend(trait_impls.for_trait(trait_.id).map(Self::from)); + } + } all } @@ -3683,7 +3713,7 @@ impl Impl { let macro_file = src.file_id.macro_file()?; let loc = macro_file.macro_call_id.lookup(db.upcast()); let (derive_attr, derive_index) = match loc.kind { - MacroCallKind::Derive { ast_id, derive_attr_index, derive_index } => { + MacroCallKind::Derive { ast_id, derive_attr_index, derive_index, .. } => { let module_id = self.id.lookup(db.upcast()).container; ( db.crate_def_map(module_id.krate())[module_id.local_id] @@ -4114,6 +4144,7 @@ impl Type { // FIXME: this code is not covered in tests. unknown_const_as_generic(ty.clone()) } + ParamKind::Lifetime => error_lifetime().cast(Interner), } }) .build(); @@ -4144,6 +4175,7 @@ impl Type { match it { ParamKind::Type => args.next().unwrap().ty.clone().cast(Interner), ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), + ParamKind::Lifetime => error_lifetime().cast(Interner), } }) .build(); diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs index 102e0ca4c3d7f..63b2a2506f895 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs @@ -177,7 +177,7 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( // Note that we need special case for 0 param constructors because of multi cartesian // product let variant_exprs: Vec = if param_exprs.is_empty() { - vec![Expr::Variant { variant, generics: generics.clone(), params: Vec::new() }] + vec![Expr::Variant { variant, generics, params: Vec::new() }] } else { param_exprs .into_iter() @@ -462,7 +462,7 @@ pub(super) fn free_function<'a, DB: HirDatabase>( /// # Impl method tactic /// -/// Attempts to to call methods on types from lookup table. +/// Attempts to call methods on types from lookup table. /// This includes both functions from direct impl blocks as well as functions from traits. /// Methods defined in impl blocks that are generic and methods that are themselves have /// generics are ignored for performance reasons. 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 d111005c2ec44..65ce3e822c5b5 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 @@ -5617,7 +5617,7 @@ fn func(i: Struct<'_, T>) { fun_name(i); } -fn $0fun_name(i: Struct<'_, T>) { +fn $0fun_name(i: Struct<'static, T>) { foo(i); } "#, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs index 38f40b8d58b4c..2150003bc1437 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs @@ -614,7 +614,7 @@ struct Foo<'a, T> { } impl<'a, T> Foo<'a, T> { - $0fn bar(self, mut b: Vec<&'a Bar<'_, T>>) -> &'a Bar<'_, T> { + $0fn bar(self, mut b: Vec<&'a Bar<'a, T>>) -> &'a Bar<'a, T> { self.field.bar(b) } } 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 24a1f9492e226..a4f092cc498ef 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 @@ -961,11 +961,11 @@ struct Foo { field: i32 } impl Foo { fn foo(&self) { $0 } }"#, expect![[r#" fd self.field i32 + me self.foo() fn(&self) lc self &Foo sp Self Foo st Foo Foo bt u32 u32 - me self.foo() fn(&self) "#]], ); check( @@ -975,11 +975,11 @@ struct Foo(i32); impl Foo { fn foo(&mut self) { $0 } }"#, expect![[r#" fd self.0 i32 + me self.foo() fn(&mut self) lc self &mut Foo sp Self Foo st Foo Foo bt u32 u32 - me self.foo() fn(&mut self) "#]], ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs index 7946784150224..c48672e80aca1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -186,11 +186,11 @@ fn add_function_impl( if func.assoc_fn_params(ctx.db).is_empty() { "" } else { ".." } ); - let completion_kind = if func.has_self_param(ctx.db) { - CompletionItemKind::Method + let completion_kind = CompletionItemKind::SymbolKind(if func.has_self_param(ctx.db) { + SymbolKind::Method } else { - CompletionItemKind::SymbolKind(SymbolKind::Function) - }; + SymbolKind::Function + }); let mut item = CompletionItem::new(completion_kind, replacement_range, label); item.lookup_by(format!("fn {}", fn_name.display(ctx.db))) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs index ed32a5db23e73..1322c05e30e97 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs @@ -75,8 +75,8 @@ impl Future for A {} fn foo(a: A) { a.$0 } "#, expect![[r#" - kw await expr.await me into_future() (as IntoFuture) fn(self) -> ::IntoFuture + kw await expr.await sn box Box::new(expr) sn call function(expr) sn dbg dbg!(expr) @@ -102,8 +102,8 @@ fn foo() { } "#, expect![[r#" - kw await expr.await me into_future() (use core::future::IntoFuture) fn(self) -> ::IntoFuture + kw await expr.await sn box Box::new(expr) sn call function(expr) sn dbg dbg!(expr) @@ -131,8 +131,8 @@ impl IntoFuture for A {} fn foo(a: A) { a.$0 } "#, expect![[r#" - kw await expr.await me into_future() (as IntoFuture) fn(self) -> ::IntoFuture + kw await expr.await sn box Box::new(expr) sn call function(expr) sn dbg dbg!(expr) 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 aa22155feffe2..995e3f482532e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -540,7 +540,7 @@ impl CompletionContext<'_> { /// Whether the given trait is an operator trait or not. pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool { match trait_.attrs(self.db).lang() { - Some(lang) => OP_TRAIT_LANG_NAMES.contains(&lang.as_str()), + Some(lang) => OP_TRAIT_LANG_NAMES.contains(&lang), None => false, } } 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 357060817c7ba..b9a2c383bdd1b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -342,7 +342,6 @@ pub enum CompletionItemKind { BuiltinType, InferredType, Keyword, - Method, Snippet, UnresolvedReference, Expression, @@ -369,6 +368,7 @@ impl CompletionItemKind { SymbolKind::LifetimeParam => "lt", SymbolKind::Local => "lc", SymbolKind::Macro => "ma", + SymbolKind::Method => "me", SymbolKind::ProcMacro => "pm", SymbolKind::Module => "md", SymbolKind::SelfParam => "sp", @@ -388,7 +388,6 @@ impl CompletionItemKind { CompletionItemKind::BuiltinType => "bt", CompletionItemKind::InferredType => "it", CompletionItemKind::Keyword => "kw", - CompletionItemKind::Method => "me", CompletionItemKind::Snippet => "sn", CompletionItemKind::UnresolvedReference => "??", CompletionItemKind::Expression => "ex", 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 6d1a5a0bc5295..ca0424809edeb 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -312,7 +312,7 @@ pub(crate) fn render_expr( None => ctx.source_range(), }; - let mut item = CompletionItem::new(CompletionItemKind::Expression, source_range, label.clone()); + let mut item = CompletionItem::new(CompletionItemKind::Expression, source_range, label); let snippet = format!( "{}$0", @@ -677,10 +677,11 @@ mod tests { #[track_caller] fn check_function_relevance(ra_fixture: &str, expect: Expect) { - let actual: Vec<_> = do_completion(ra_fixture, CompletionItemKind::Method) - .into_iter() - .map(|item| (item.detail.unwrap_or_default(), item.relevance.function)) - .collect(); + let actual: Vec<_> = + do_completion(ra_fixture, CompletionItemKind::SymbolKind(SymbolKind::Method)) + .into_iter() + .map(|item| (item.detail.unwrap_or_default(), item.relevance.function)) + .collect(); expect.assert_debug_eq(&actual); } @@ -1392,7 +1393,10 @@ impl S { /// Method docs fn bar(self) { self.$0 } }"#, - &[CompletionItemKind::Method, CompletionItemKind::SymbolKind(SymbolKind::Field)], + &[ + CompletionItemKind::SymbolKind(SymbolKind::Method), + CompletionItemKind::SymbolKind(SymbolKind::Field), + ], expect![[r#" [ CompletionItem { @@ -1400,7 +1404,9 @@ impl S { source_range: 94..94, delete: 94..94, insert: "bar()$0", - kind: Method, + kind: SymbolKind( + Method, + ), lookup: "bar", detail: "fn(self)", documentation: Documentation( @@ -1520,7 +1526,7 @@ impl S { } fn foo(s: S) { s.$0 } "#, - CompletionItemKind::Method, + CompletionItemKind::SymbolKind(SymbolKind::Method), expect![[r#" [ CompletionItem { @@ -1528,7 +1534,9 @@ fn foo(s: S) { s.$0 } source_range: 81..81, delete: 81..81, insert: "the_method()$0", - kind: Method, + kind: SymbolKind( + Method, + ), lookup: "the_method", detail: "fn(&self)", relevance: CompletionRelevance { @@ -2408,7 +2416,10 @@ impl Foo { fn baz(&self) -> u32 { 0 } } fn foo(f: Foo) { let _: &u32 = f.b$0 } "#, - &[CompletionItemKind::Method, CompletionItemKind::SymbolKind(SymbolKind::Field)], + &[ + CompletionItemKind::SymbolKind(SymbolKind::Method), + CompletionItemKind::SymbolKind(SymbolKind::Field), + ], expect![[r#" [ CompletionItem { @@ -2416,7 +2427,9 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 } source_range: 109..110, delete: 109..110, insert: "baz()$0", - kind: Method, + kind: SymbolKind( + Method, + ), lookup: "baz", detail: "fn(&self) -> u32", relevance: CompletionRelevance { @@ -2631,7 +2644,7 @@ fn main() { let _: bool = (9 > 2).not$0; } "#, - &[CompletionItemKind::Snippet, CompletionItemKind::Method], + &[CompletionItemKind::Snippet, CompletionItemKind::SymbolKind(SymbolKind::Method)], expect![[r#" sn not [snippet] me not() (use ops::Not) [type_could_unify+requires_import] @@ -2664,7 +2677,7 @@ fn main() { S.$0 } "#, - &[CompletionItemKind::Snippet, CompletionItemKind::Method], + &[CompletionItemKind::Snippet, CompletionItemKind::SymbolKind(SymbolKind::Method)], expect![[r#" me f() [] sn ref [] @@ -2907,7 +2920,7 @@ fn main() { } "#, &[ - CompletionItemKind::Method, + CompletionItemKind::SymbolKind(SymbolKind::Method), CompletionItemKind::SymbolKind(SymbolKind::Field), CompletionItemKind::SymbolKind(SymbolKind::Function), ], @@ -2918,7 +2931,9 @@ fn main() { source_range: 193..193, delete: 193..193, insert: "flush()$0", - kind: Method, + kind: SymbolKind( + Method, + ), lookup: "flush", detail: "fn(&self)", relevance: CompletionRelevance { @@ -2941,7 +2956,9 @@ fn main() { source_range: 193..193, delete: 193..193, insert: "write()$0", - kind: Method, + kind: SymbolKind( + Method, + ), lookup: "write", detail: "fn(&self)", relevance: CompletionRelevance { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index cf9fe1ab30728..1634b0a920611 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -68,11 +68,11 @@ fn render( }; let has_self_param = func.self_param(db).is_some(); let mut item = CompletionItem::new( - if has_self_param { - CompletionItemKind::Method + CompletionItemKind::SymbolKind(if has_self_param { + SymbolKind::Method } else { - CompletionItemKind::SymbolKind(SymbolKind::Function) - }, + SymbolKind::Function + }), ctx.source_range(), call.clone(), ); 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 7749fac40b9dc..a653314233d39 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 @@ -127,6 +127,7 @@ impl Unit { en Enum Enum fn function() fn() fn local_func() fn() + me self.foo() fn(self) lc self Unit ma makro!(…) macro_rules! makro md module @@ -166,7 +167,6 @@ impl Unit { kw use kw while kw while let - me self.foo() fn(self) sn macro_rules sn pd sn ppd diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs index 46a3e97d3e92d..3718dff56e88f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs @@ -19,7 +19,7 @@ struct Foo<'lt, T, const C: usize> where $0 {} en Enum Enum ma makro!(…) macro_rules! makro md module - st Foo<…> Foo<'_, {unknown}, _> + st Foo<…> Foo<'static, {unknown}, _> st Record Record st Tuple Tuple st Unit Unit @@ -92,7 +92,7 @@ struct Foo<'lt, T, const C: usize> where for<'a> $0 {} en Enum Enum ma makro!(…) macro_rules! makro md module - st Foo<…> Foo<'_, {unknown}, _> + st Foo<…> Foo<'static, {unknown}, _> st Record Record st Tuple Tuple st Unit Unit 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 ff32eccfbff4c..69d8fe91040b0 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 @@ -1,6 +1,7 @@ //! Tests that don't fit into a specific category. use expect_test::{expect, Expect}; +use ide_db::SymbolKind; use crate::{ tests::{ @@ -316,15 +317,15 @@ trait Sub: Super { fn foo() { T::$0 } "#, expect![[r#" - ct C2 (as Sub) const C2: () - ct CONST (as Super) const CONST: u8 - fn func() (as Super) fn() - fn subfunc() (as Sub) fn() - ta SubTy (as Sub) type SubTy - ta Ty (as Super) type Ty - me method(…) (as Super) fn(&self) - me submethod(…) (as Sub) fn(&self) - "#]], + ct C2 (as Sub) const C2: () + ct CONST (as Super) const CONST: u8 + fn func() (as Super) fn() + fn subfunc() (as Sub) fn() + me method(…) (as Super) fn(&self) + me submethod(…) (as Sub) fn(&self) + ta SubTy (as Sub) type SubTy + ta Ty (as Super) type Ty + "#]], ); } @@ -356,15 +357,15 @@ impl Sub for Wrap { } "#, expect![[r#" - ct C2 (as Sub) const C2: () - ct CONST (as Super) const CONST: u8 - fn func() (as Super) fn() - fn subfunc() (as Sub) fn() - ta SubTy (as Sub) type SubTy - ta Ty (as Super) type Ty - me method(…) (as Super) fn(&self) - me submethod(…) (as Sub) fn(&self) - "#]], + ct C2 (as Sub) const C2: () + ct CONST (as Super) const CONST: u8 + fn func() (as Super) fn() + fn subfunc() (as Sub) fn() + me method(…) (as Super) fn(&self) + me submethod(…) (as Sub) fn(&self) + ta SubTy (as Sub) type SubTy + ta Ty (as Super) type Ty + "#]], ); } @@ -555,10 +556,10 @@ impl Foo { } "#, expect![[r#" - ev Bar Bar - ev Baz Baz - me foo(…) fn(self) - "#]], + me foo(…) fn(self) + ev Bar Bar + ev Baz Baz + "#]], ); } @@ -1399,7 +1400,7 @@ fn main() { bar.b$0 } "#, - CompletionItemKind::Method, + CompletionItemKind::SymbolKind(SymbolKind::Method), expect!("const fn(&'foo mut self, &Foo) -> !"), expect!("pub const fn baz<'foo>(&'foo mut self, x: &'foo Foo) -> !"), ); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs index db4ac9381cedb..97709728656da 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs @@ -20,8 +20,8 @@ struct Foo<'lt, T, const C: usize> { en Enum Enum ma makro!(…) macro_rules! makro md module - sp Self Foo<'_, {unknown}, _> - st Foo<…> Foo<'_, {unknown}, _> + sp Self Foo<'static, {unknown}, _> + st Foo<…> Foo<'static, {unknown}, _> st Record Record st Tuple Tuple st Unit Unit @@ -45,8 +45,8 @@ struct Foo<'lt, T, const C: usize>(f$0); en Enum Enum ma makro!(…) macro_rules! makro md module - sp Self Foo<'_, {unknown}, _> - st Foo<…> Foo<'_, {unknown}, _> + sp Self Foo<'static, {unknown}, _> + st Foo<…> Foo<'static, {unknown}, _> st Record Record st Tuple Tuple st Unit Unit 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 0d5a93f7b8e50..357209ceb0b4b 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -346,6 +346,7 @@ pub enum SymbolKind { Enum, Field, Function, + Method, Impl, Label, LifetimeParam, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml index 8ccea99e9e13d..edd05009332e5 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml @@ -26,6 +26,7 @@ text-edit.workspace = true cfg.workspace = true hir.workspace = true ide-db.workspace = true +paths.workspace = true [dev-dependencies] expect-test = "1.4.0" diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs index 67daa172b27e5..045154614f80f 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs @@ -23,6 +23,7 @@ mod tests { }, DiagnosticsConfig, }; + use test_utils::skip_slow_tests; #[track_caller] fn check_diagnostics_no_bails(ra_fixture: &str) { @@ -1004,6 +1005,32 @@ fn f() { ); } + #[test] + fn exponential_match() { + if skip_slow_tests() { + return; + } + // Constructs a match where match checking takes exponential time. Ensures we bail early. + use std::fmt::Write; + let struct_arity = 50; + let mut code = String::new(); + write!(code, "struct BigStruct {{").unwrap(); + for i in 0..struct_arity { + write!(code, " field{i}: bool,").unwrap(); + } + write!(code, "}}").unwrap(); + write!(code, "fn big_match(s: BigStruct) {{").unwrap(); + write!(code, " match s {{").unwrap(); + for i in 0..struct_arity { + write!(code, " BigStruct {{ field{i}: true, ..}} => {{}},").unwrap(); + write!(code, " BigStruct {{ field{i}: false, ..}} => {{}},").unwrap(); + } + write!(code, " _ => {{}},").unwrap(); + write!(code, " }}").unwrap(); + write!(code, "}}").unwrap(); + check_diagnostics_no_bails(&code); + } + mod rust_unstable { use super::*; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 34a0038295fd9..00352266ddbdc 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -7,7 +7,11 @@ use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: need-mut // // This diagnostic is triggered on mutating an immutable variable. -pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Diagnostic { +pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Option { + if d.span.file_id.macro_file().is_some() { + // FIXME: Our infra can't handle allow from within macro expansions rn + return None; + } let fixes = (|| { if d.local.is_ref(ctx.sema.db) { // There is no simple way to add `mut` to `ref x` and `ref mut x` @@ -29,24 +33,30 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Diagno use_range, )]) })(); - Diagnostic::new_with_syntax_node_ptr( - ctx, - // FIXME: `E0384` is not the only error that this diagnostic handles - DiagnosticCode::RustcHardError("E0384"), - format!( - "cannot mutate immutable variable `{}`", - d.local.name(ctx.sema.db).display(ctx.sema.db) - ), - d.span, + Some( + Diagnostic::new_with_syntax_node_ptr( + ctx, + // FIXME: `E0384` is not the only error that this diagnostic handles + DiagnosticCode::RustcHardError("E0384"), + format!( + "cannot mutate immutable variable `{}`", + d.local.name(ctx.sema.db).display(ctx.sema.db) + ), + d.span, + ) + .with_fixes(fixes), ) - .with_fixes(fixes) } // Diagnostic: unused-mut // // This diagnostic is triggered when a mutable variable isn't actually mutated. -pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Diagnostic { +pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Option { let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); + if ast.file_id.macro_file().is_some() { + // FIXME: Our infra can't handle allow from within macro expansions rn + return None; + } let fixes = (|| { let file_id = ast.file_id.file_id()?; let mut edit_builder = TextEdit::builder(); @@ -70,14 +80,16 @@ pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Di )]) })(); let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); - Diagnostic::new_with_syntax_node_ptr( - ctx, - DiagnosticCode::RustcLint("unused_mut"), - "variable does not need to be mutable", - ast, + Some( + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcLint("unused_mut"), + "variable does not need to be mutable", + ast, + ) + .experimental() // Not supporting `#[allow(unused_mut)]` in proc macros leads to false positive. + .with_fixes(fixes), ) - .experimental() // Not supporting `#[allow(unused_mut)]` in proc macros leads to false positive. - .with_fixes(fixes) } pub(super) fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs index 7a040e46e3386..d831878044d32 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs @@ -12,7 +12,12 @@ use crate::{adjusted_display_range, fix, Diagnostic, DiagnosticCode, Diagnostics pub(crate) fn remove_trailing_return( ctx: &DiagnosticsContext<'_>, d: &RemoveTrailingReturn, -) -> Diagnostic { +) -> Option { + if d.return_expr.file_id.macro_file().is_some() { + // FIXME: Our infra can't handle allow from within macro expansions rn + return None; + } + let display_range = adjusted_display_range(ctx, d.return_expr, &|return_expr| { return_expr .syntax() @@ -20,12 +25,14 @@ pub(crate) fn remove_trailing_return( .and_then(ast::ExprStmt::cast) .map(|stmt| stmt.syntax().text_range()) }); - Diagnostic::new( - DiagnosticCode::Clippy("needless_return"), - "replace return ; with ", - display_range, + Some( + Diagnostic::new( + DiagnosticCode::Clippy("needless_return"), + "replace return ; with ", + display_range, + ) + .with_fixes(fixes(ctx, d)), ) - .with_fixes(fixes(ctx, d)) } fn fixes(ctx: &DiagnosticsContext<'_>, d: &RemoveTrailingReturn) -> Option> { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs index f68e698238562..448df1ca163a6 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs @@ -21,23 +21,30 @@ use crate::{ pub(crate) fn remove_unnecessary_else( ctx: &DiagnosticsContext<'_>, d: &RemoveUnnecessaryElse, -) -> Diagnostic { +) -> Option { + if d.if_expr.file_id.macro_file().is_some() { + // FIXME: Our infra can't handle allow from within macro expansions rn + return None; + } + let display_range = adjusted_display_range(ctx, d.if_expr, &|if_expr| { if_expr.else_token().as_ref().map(SyntaxToken::text_range) }); - Diagnostic::new( - DiagnosticCode::Ra("remove-unnecessary-else", Severity::WeakWarning), - "remove unnecessary else block", - display_range, + Some( + Diagnostic::new( + DiagnosticCode::Ra("remove-unnecessary-else", Severity::WeakWarning), + "remove unnecessary else block", + display_range, + ) + .experimental() + .with_fixes(fixes(ctx, d)), ) - .experimental() - .with_fixes(fixes(ctx, d)) } fn fixes(ctx: &DiagnosticsContext<'_>, d: &RemoveUnnecessaryElse) -> Option> { let root = ctx.sema.db.parse_or_expand(d.if_expr.file_id); let if_expr = d.if_expr.value.to_node(&root); - let if_expr = ctx.sema.original_ast_node(if_expr.clone())?; + let if_expr = ctx.sema.original_ast_node(if_expr)?; let mut indent = IndentLevel::from_node(if_expr.syntax()); let has_parent_if_expr = if_expr.syntax().parent().and_then(ast::IfExpr::cast).is_some(); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs index becc24ab21ecb..b9327f85567cb 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs @@ -8,6 +8,7 @@ use ide_db::{ source_change::SourceChange, RootDatabase, }; +use paths::Utf8Component; use syntax::{ ast::{self, edit::IndentLevel, HasModuleItem, HasName}, AstNode, TextRange, @@ -84,10 +85,10 @@ fn fixes(ctx: &DiagnosticsContext<'_>, file_id: FileId) -> Option> { // try resolving the relative difference of the paths as inline modules let mut current = root_module; - for ele in rel.as_ref().components() { + for ele in rel.as_utf8_path().components() { let seg = match ele { - std::path::Component::Normal(seg) => seg.to_str()?, - std::path::Component::RootDir => continue, + Utf8Component::Normal(seg) => seg, + Utf8Component::RootDir => continue, // shouldn't occur _ => continue 'crates, }; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs index a9e1d07d7c52d..cd251faab9ad5 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs @@ -14,18 +14,24 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; pub(crate) fn unused_variables( ctx: &DiagnosticsContext<'_>, d: &hir::UnusedVariable, -) -> Diagnostic { +) -> Option { let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); + if ast.file_id.macro_file().is_some() { + // FIXME: Our infra can't handle allow from within macro expansions rn + return None; + } let diagnostic_range = ctx.sema.diagnostics_display_range(ast); let var_name = d.local.primary_source(ctx.sema.db).syntax().to_string(); - Diagnostic::new_with_syntax_node_ptr( - ctx, - DiagnosticCode::RustcLint("unused_variables"), - "unused variable", - ast, + Some( + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcLint("unused_variables"), + "unused variable", + ast, + ) + .with_fixes(fixes(&var_name, diagnostic_range, ast.file_id.is_macro())) + .experimental(), ) - .with_fixes(fixes(&var_name, diagnostic_range, ast.file_id.is_macro())) - .experimental() } fn fixes(var_name: &String, diagnostic_range: FileRange, is_in_marco: bool) -> Option> { @@ -47,7 +53,7 @@ fn fixes(var_name: &String, diagnostic_range: FileRange, is_in_marco: bool) -> O #[cfg(test)] mod tests { - use crate::tests::{check_diagnostics, check_fix, check_no_fix}; + use crate::tests::{check_diagnostics, check_fix}; #[test] fn unused_variables_simple() { @@ -193,7 +199,7 @@ fn main() { #[test] fn no_fix_for_marco() { - check_no_fix( + check_diagnostics( r#" macro_rules! my_macro { () => { @@ -202,7 +208,7 @@ macro_rules! my_macro { } fn main() { - $0my_macro!(); + my_macro!(); } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 0df6f0e0373c8..270cf844c65b9 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -330,7 +330,6 @@ pub fn diagnostics( } for diag in diags { - #[rustfmt::skip] let d = match diag { AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d), AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) { @@ -361,7 +360,10 @@ pub fn diagnostics( AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d), AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d), AnyDiagnostic::MovedOutOfRef(d) => handlers::moved_out_of_ref::moved_out_of_ref(&ctx, &d), - AnyDiagnostic::NeedMut(d) => handlers::mutability_errors::need_mut(&ctx, &d), + AnyDiagnostic::NeedMut(d) => match handlers::mutability_errors::need_mut(&ctx, &d) { + Some(it) => it, + None => continue, + }, AnyDiagnostic::NonExhaustiveLet(d) => handlers::non_exhaustive_let::non_exhaustive_let(&ctx, &d), AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d), AnyDiagnostic::PrivateAssocItem(d) => handlers::private_assoc_item::private_assoc_item(&ctx, &d), @@ -385,12 +387,24 @@ pub fn diagnostics( AnyDiagnostic::UnresolvedMethodCall(d) => handlers::unresolved_method::unresolved_method(&ctx, &d), AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&ctx, &d), AnyDiagnostic::UnresolvedProcMacro(d) => handlers::unresolved_proc_macro::unresolved_proc_macro(&ctx, &d, config.proc_macros_enabled, config.proc_attr_macros_enabled), - AnyDiagnostic::UnusedMut(d) => handlers::mutability_errors::unused_mut(&ctx, &d), - AnyDiagnostic::UnusedVariable(d) => handlers::unused_variables::unused_variables(&ctx, &d), + AnyDiagnostic::UnusedMut(d) => match handlers::mutability_errors::unused_mut(&ctx, &d) { + Some(it) => it, + None => continue, + }, + AnyDiagnostic::UnusedVariable(d) => match handlers::unused_variables::unused_variables(&ctx, &d) { + Some(it) => it, + None => continue, + }, AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d), AnyDiagnostic::MismatchedTupleStructPatArgCount(d) => handlers::mismatched_arg_count::mismatched_tuple_struct_pat_arg_count(&ctx, &d), - AnyDiagnostic::RemoveTrailingReturn(d) => handlers::remove_trailing_return::remove_trailing_return(&ctx, &d), - AnyDiagnostic::RemoveUnnecessaryElse(d) => handlers::remove_unnecessary_else::remove_unnecessary_else(&ctx, &d), + AnyDiagnostic::RemoveTrailingReturn(d) => match handlers::remove_trailing_return::remove_trailing_return(&ctx, &d) { + Some(it) => it, + None => continue, + }, + AnyDiagnostic::RemoveUnnecessaryElse(d) => match handlers::remove_unnecessary_else::remove_unnecessary_else(&ctx, &d) { + Some(it) => it, + None => continue, + }, }; res.push(d) } @@ -399,9 +413,9 @@ pub fn diagnostics( .iter_mut() .filter_map(|it| { Some(( - it.main_node - .map(|ptr| ptr.map(|node| node.to_node(&ctx.sema.parse_or_expand(ptr.file_id)))) - .clone()?, + it.main_node.map(|ptr| { + ptr.map(|node| node.to_node(&ctx.sema.parse_or_expand(ptr.file_id))) + })?, it, )) }) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs index dcaa212089233..bb5c2b791392d 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs @@ -283,6 +283,10 @@ fn test_disabled_diagnostics() { #[test] fn minicore_smoke_test() { + if test_utils::skip_slow_tests() { + return; + } + fn check(minicore: MiniCore) { let source = minicore.source_code(); let mut config = DiagnosticsConfig::test_sample(); diff --git a/src/tools/rust-analyzer/crates/ide/Cargo.toml b/src/tools/rust-analyzer/crates/ide/Cargo.toml index aca7d613e11fc..a535015a27f09 100644 --- a/src/tools/rust-analyzer/crates/ide/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide/Cargo.toml @@ -36,6 +36,7 @@ ide-ssr.workspace = true profile.workspace = true stdx.workspace = true syntax.workspace = true +span.workspace = true text-edit.workspace = true # ide should depend only on the top-level `hir` package. if you need # something from some `hir-xxx` subpackage, reexport the API via `hir`. 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 d10bdca50d81e..64b4ccc5bd81c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -5,8 +5,6 @@ mod tests; mod intra_doc_links; -use std::ffi::OsStr; - use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag}; use pulldown_cmark_to_cmark::{cmark_resume_with_options, Options as CMarkOptions}; use stdx::format_to; @@ -134,8 +132,8 @@ pub(crate) fn remove_links(markdown: &str) -> String { pub(crate) fn external_docs( db: &RootDatabase, FilePosition { file_id, offset }: FilePosition, - target_dir: Option<&OsStr>, - sysroot: Option<&OsStr>, + target_dir: Option<&str>, + sysroot: Option<&str>, ) -> Option { let sema = &Semantics::new(db); let file = sema.parse(file_id).syntax().clone(); @@ -331,8 +329,8 @@ fn broken_link_clone_cb(link: BrokenLink<'_>) -> Option<(CowStr<'_>, CowStr<'_>) fn get_doc_links( db: &RootDatabase, def: Definition, - target_dir: Option<&OsStr>, - sysroot: Option<&OsStr>, + target_dir: Option<&str>, + sysroot: Option<&str>, ) -> DocumentationLinks { let join_url = |base_url: Option, path: &str| -> Option { base_url.and_then(|url| url.join(path).ok()) @@ -479,15 +477,13 @@ fn map_links<'e>( fn get_doc_base_urls( db: &RootDatabase, def: Definition, - target_dir: Option<&OsStr>, - sysroot: Option<&OsStr>, + target_dir: Option<&str>, + sysroot: Option<&str>, ) -> (Option, Option) { let local_doc = target_dir - .and_then(|path| path.to_str()) .and_then(|path| Url::parse(&format!("file:///{path}/")).ok()) .and_then(|it| it.join("doc/").ok()); let system_doc = sysroot - .and_then(|it| it.to_str()) .map(|sysroot| format!("file:///{sysroot}/share/doc/rust/html/")) .and_then(|it| Url::parse(&it).ok()); diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs index 60e8d29a7163a..ac44908a90214 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs @@ -1,4 +1,4 @@ -use std::{ffi::OsStr, iter}; +use std::iter; use expect_test::{expect, Expect}; use hir::Semantics; @@ -18,10 +18,10 @@ use crate::{ fn check_external_docs( ra_fixture: &str, - target_dir: Option<&OsStr>, + target_dir: Option<&str>, expect_web_url: Option, expect_local_url: Option, - sysroot: Option<&OsStr>, + sysroot: Option<&str>, ) { let (analysis, position) = fixture::position(ra_fixture); let links = analysis.external_docs(position, target_dir, sysroot).unwrap(); @@ -127,10 +127,10 @@ fn external_docs_doc_builtin_type() { //- /main.rs crate:foo let x: u3$02 = 0; "#, - Some(OsStr::new("/home/user/project")), + Some("/home/user/project"), Some(expect![[r#"https://doc.rust-lang.org/nightly/core/primitive.u32.html"#]]), Some(expect![[r#"file:///sysroot/share/doc/rust/html/core/primitive.u32.html"#]]), - Some(OsStr::new("/sysroot")), + Some("/sysroot"), ); } @@ -143,10 +143,10 @@ use foo$0::Foo; //- /lib.rs crate:foo pub struct Foo; "#, - Some(OsStr::new("/home/user/project")), + Some("/home/user/project"), Some(expect![[r#"https://docs.rs/foo/*/foo/index.html"#]]), Some(expect![[r#"file:///home/user/project/doc/foo/index.html"#]]), - Some(OsStr::new("/sysroot")), + Some("/sysroot"), ); } @@ -157,10 +157,10 @@ fn external_docs_doc_url_std_crate() { //- /main.rs crate:std use self$0; "#, - Some(OsStr::new("/home/user/project")), + Some("/home/user/project"), Some(expect!["https://doc.rust-lang.org/stable/std/index.html"]), Some(expect!["file:///sysroot/share/doc/rust/html/std/index.html"]), - Some(OsStr::new("/sysroot")), + Some("/sysroot"), ); } @@ -171,10 +171,10 @@ fn external_docs_doc_url_struct() { //- /main.rs crate:foo pub struct Fo$0o; "#, - Some(OsStr::new("/home/user/project")), + Some("/home/user/project"), Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]), Some(expect![[r#"file:///home/user/project/doc/foo/struct.Foo.html"#]]), - Some(OsStr::new("/sysroot")), + Some("/sysroot"), ); } @@ -185,10 +185,10 @@ fn external_docs_doc_url_windows_backslash_path() { //- /main.rs crate:foo pub struct Fo$0o; "#, - Some(OsStr::new(r"C:\Users\user\project")), + Some(r"C:\Users\user\project"), Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]), Some(expect![[r#"file:///C:/Users/user/project/doc/foo/struct.Foo.html"#]]), - Some(OsStr::new("/sysroot")), + Some("/sysroot"), ); } @@ -199,10 +199,10 @@ fn external_docs_doc_url_windows_slash_path() { //- /main.rs crate:foo pub struct Fo$0o; "#, - Some(OsStr::new(r"C:/Users/user/project")), + Some("C:/Users/user/project"), Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]), Some(expect![[r#"file:///C:/Users/user/project/doc/foo/struct.Foo.html"#]]), - Some(OsStr::new("/sysroot")), + Some("/sysroot"), ); } diff --git a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs index 0e790e14205f4..813691540f92f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs +++ b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs @@ -134,15 +134,22 @@ fn structure_node(node: &SyntaxNode) -> Option { if let Some(type_param_list) = it.generic_param_list() { collapse_ws(type_param_list.syntax(), &mut detail); } - if let Some(param_list) = it.param_list() { + let has_self_param = if let Some(param_list) = it.param_list() { collapse_ws(param_list.syntax(), &mut detail); - } + param_list.self_param().is_some() + } else { + false + }; if let Some(ret_type) = it.ret_type() { detail.push(' '); collapse_ws(ret_type.syntax(), &mut detail); } - decl_with_detail(&it, Some(detail), StructureNodeKind::SymbolKind(SymbolKind::Function)) + decl_with_detail(&it, Some(detail), StructureNodeKind::SymbolKind(if has_self_param { + SymbolKind::Method + } else { + SymbolKind::Function + })) }, ast::Struct(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Struct)), ast::Union(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Union)), 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 8a12cbacccc70..76e5e9dd9288a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs @@ -337,6 +337,77 @@ impl Tr for S { const C: usize = 4; //^ } +"#, + ); + } + + #[test] + fn goto_adt_implementation_inside_block() { + check( + r#" +//- minicore: copy, derive +trait Bar {} + +fn test() { + #[derive(Copy)] + //^^^^^^^^^^^^^^^ + struct Foo$0; + + impl Foo {} + //^^^ + + trait Baz {} + + impl Bar for Foo {} + //^^^ + + impl Baz for Foo {} + //^^^ +} +"#, + ); + } + + #[test] + fn goto_trait_implementation_inside_block() { + check( + r#" +struct Bar; + +fn test() { + trait Foo$0 {} + + struct Baz; + + impl Foo for Bar {} + //^^^ + + impl Foo for Baz {} + //^^^ +} +"#, + ); + check( + r#" +struct Bar; + +fn test() { + trait Foo { + fn foo$0() {} + } + + struct Baz; + + impl Foo for Bar { + fn foo() {} + //^^^ + } + + impl Foo for Baz { + fn foo() {} + //^^^ + } +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 8f4c629b58130..822751c0e4ce7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -33,6 +33,7 @@ pub struct HoverConfig { pub keywords: bool, pub format: HoverDocFormat, pub max_trait_assoc_items_count: Option, + pub max_struct_field_count: Option, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] 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 63777d4910503..abedbff831a50 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -410,6 +410,9 @@ pub(super) fn definition( Definition::Trait(trait_) => { trait_.display_limited(db, config.max_trait_assoc_items_count).to_string() } + Definition::Adt(Adt::Struct(struct_)) => { + struct_.display_limited(db, config.max_struct_field_count).to_string() + } _ => def.label(db), }; let docs = def.docs(db, famous_defs); 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 4451e31870f26..08925fcdff580 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -18,6 +18,7 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig { format: HoverDocFormat::Markdown, keywords: true, max_trait_assoc_items_count: None, + max_struct_field_count: None, }; fn check_hover_no_result(ra_fixture: &str) { @@ -49,6 +50,28 @@ fn check(ra_fixture: &str, expect: Expect) { expect.assert_eq(&actual) } +#[track_caller] +fn check_hover_struct_limit(count: usize, ra_fixture: &str, expect: Expect) { + let (analysis, position) = fixture::position(ra_fixture); + let hover = analysis + .hover( + &HoverConfig { + links_in_hover: true, + max_struct_field_count: Some(count), + ..HOVER_BASE_CONFIG + }, + FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, + ) + .unwrap() + .unwrap(); + + let content = analysis.db.file_text(position.file_id); + let hovered_element = &content[hover.range]; + + let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup); + expect.assert_eq(&actual) +} + #[track_caller] fn check_assoc_count(count: usize, ra_fixture: &str, expect: Expect) { let (analysis, position) = fixture::position(ra_fixture); @@ -853,9 +876,7 @@ struct Foo$0 { field: u32 } ```rust // size = 4, align = 4 - struct Foo { - field: u32, - } + struct Foo ``` "#]], ); @@ -875,8 +896,74 @@ struct Foo$0 where u32: Copy { field: u32 } struct Foo where u32: Copy, - { - field: u32, + ``` + "#]], + ); +} + +#[test] +fn hover_record_struct_limit() { + check_hover_struct_limit( + 3, + r#" + struct Foo$0 { a: u32, b: i32, c: i32 } + "#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 12 (0xC), align = 4 + struct Foo { + a: u32, + b: i32, + c: i32, + } + ``` + "#]], + ); + check_hover_struct_limit( + 3, + r#" + struct Foo$0 { a: u32 } + "#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 4, align = 4 + struct Foo { + a: u32, + } + ``` + "#]], + ); + check_hover_struct_limit( + 3, + r#" + struct Foo$0 { a: u32, b: i32, c: i32, d: u32 } + "#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 16 (0x10), align = 4 + struct Foo { + a: u32, + b: i32, + c: i32, + /* … */ } ``` "#]], @@ -1344,9 +1431,7 @@ impl Thing { ``` ```rust - struct Thing { - x: u32, - } + struct Thing ``` "#]], ); @@ -1365,9 +1450,7 @@ impl Thing { ``` ```rust - struct Thing { - x: u32, - } + struct Thing ``` "#]], ); @@ -2599,7 +2682,7 @@ fn main() { let s$0t = S{ f1:0 }; } focus_range: 7..8, name: "S", kind: Struct, - description: "struct S {\n f1: u32,\n}", + description: "struct S", }, }, ], @@ -2645,7 +2728,7 @@ fn main() { let s$0t = S{ f1:Arg(0) }; } focus_range: 24..25, name: "S", kind: Struct, - description: "struct S {\n f1: T,\n}", + description: "struct S", }, }, ], @@ -2704,7 +2787,7 @@ fn main() { let s$0t = S{ f1: S{ f1: Arg(0) } }; } focus_range: 24..25, name: "S", kind: Struct, - description: "struct S {\n f1: T,\n}", + description: "struct S", }, }, ], @@ -2957,7 +3040,7 @@ fn main() { let s$0t = foo(); } focus_range: 39..41, name: "S1", kind: Struct, - description: "struct S1 {}", + description: "struct S1", }, }, HoverGotoTypeData { @@ -2970,7 +3053,7 @@ fn main() { let s$0t = foo(); } focus_range: 52..54, name: "S2", kind: Struct, - description: "struct S2 {}", + description: "struct S2", }, }, ], @@ -3061,7 +3144,7 @@ fn foo(ar$0g: &impl Foo + Bar) {} focus_range: 36..37, name: "S", kind: Struct, - description: "struct S {}", + description: "struct S", }, }, ], @@ -3161,7 +3244,7 @@ fn foo(ar$0g: &impl Foo) {} focus_range: 23..24, name: "S", kind: Struct, - description: "struct S {}", + description: "struct S", }, }, ], @@ -3198,7 +3281,7 @@ fn main() { let s$0t = foo(); } focus_range: 49..50, name: "B", kind: Struct, - description: "struct B {}", + description: "struct B", }, }, HoverGotoTypeData { @@ -3287,7 +3370,7 @@ fn foo(ar$0g: &dyn Foo) {} focus_range: 23..24, name: "S", kind: Struct, - description: "struct S {}", + description: "struct S", }, }, ], @@ -3322,7 +3405,7 @@ fn foo(a$0rg: &impl ImplTrait>>>) {} focus_range: 50..51, name: "B", kind: Struct, - description: "struct B {}", + description: "struct B", }, }, HoverGotoTypeData { @@ -3361,7 +3444,7 @@ fn foo(a$0rg: &impl ImplTrait>>>) {} focus_range: 65..66, name: "S", kind: Struct, - description: "struct S {}", + description: "struct S", }, }, ], @@ -5105,6 +5188,32 @@ fn foo(e: E) { ); } +#[test] +fn hover_const_value() { + check( + r#" +pub enum AA { + BB, +} +const CONST: AA = AA::BB; +pub fn the_function() -> AA { + CON$0ST +} +"#, + expect![[r#" + *CONST* + + ```rust + test + ``` + + ```rust + const CONST: AA = BB + ``` + "#]], + ); +} + #[test] fn array_repeat_exp() { check( @@ -7747,3 +7856,25 @@ impl Iterator for S { "#]], ); } + +#[test] +fn hover_lifetime_regression_16963() { + check( + r#" +struct Pedro$0<'a> { + hola: &'a str +} +"#, + expect![[r#" + *Pedro* + + ```rust + test + ``` + + ```rust + struct Pedro<'a> + ``` + "#]], + ) +} 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 8311e770b4b41..dda38ce77e0c5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -1,5 +1,6 @@ use std::{ fmt::{self, Write}, + hash::{BuildHasher, BuildHasherDefault}, mem::take, }; @@ -8,7 +9,7 @@ use hir::{ known, ClosureStyle, HasVisibility, HirDisplay, HirDisplayError, HirWrite, ModuleDef, ModuleDefId, Semantics, }; -use ide_db::{base_db::FileRange, famous_defs::FamousDefs, RootDatabase}; +use ide_db::{base_db::FileRange, famous_defs::FamousDefs, FxHasher, RootDatabase}; use itertools::Itertools; use smallvec::{smallvec, SmallVec}; use stdx::never; @@ -116,7 +117,7 @@ pub enum AdjustmentHintsMode { PreferPostfix, } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum InlayKind { Adjustment, BindingMode, @@ -132,7 +133,7 @@ pub enum InlayKind { RangeExclusive, } -#[derive(Debug)] +#[derive(Debug, Hash)] pub enum InlayHintPosition { Before, After, @@ -151,13 +152,23 @@ pub struct InlayHint { pub label: InlayHintLabel, /// Text edit to apply when "accepting" this inlay hint. pub text_edit: Option, - pub needs_resolve: bool, +} + +impl std::hash::Hash for InlayHint { + fn hash(&self, state: &mut H) { + self.range.hash(state); + self.position.hash(state); + self.pad_left.hash(state); + self.pad_right.hash(state); + self.kind.hash(state); + self.label.hash(state); + self.text_edit.is_some().hash(state); + } } impl InlayHint { fn closing_paren_after(kind: InlayKind, range: TextRange) -> InlayHint { InlayHint { - needs_resolve: false, range, kind, label: InlayHintLabel::from(")"), @@ -167,9 +178,9 @@ impl InlayHint { pad_right: false, } } + fn opening_paren_before(kind: InlayKind, range: TextRange) -> InlayHint { InlayHint { - needs_resolve: false, range, kind, label: InlayHintLabel::from("("), @@ -179,15 +190,19 @@ impl InlayHint { pad_right: false, } } + + pub fn needs_resolve(&self) -> bool { + self.text_edit.is_some() || self.label.needs_resolve() + } } -#[derive(Debug)] +#[derive(Debug, Hash)] pub enum InlayTooltip { String(String), Markdown(String), } -#[derive(Default)] +#[derive(Default, Hash)] pub struct InlayHintLabel { pub parts: SmallVec<[InlayHintLabelPart; 1]>, } @@ -265,6 +280,7 @@ impl fmt::Debug for InlayHintLabel { } } +#[derive(Hash)] pub struct InlayHintLabelPart { pub text: String, /// Source location represented by this label part. The client will use this to fetch the part's @@ -313,9 +329,7 @@ impl fmt::Write for InlayHintLabelBuilder<'_> { impl HirWrite for InlayHintLabelBuilder<'_> { fn start_location_link(&mut self, def: ModuleDefId) { - if self.location.is_some() { - never!("location link is already started"); - } + never!(self.location.is_some(), "location link is already started"); self.make_new_part(); let Some(location) = ModuleDef::from(def).try_to_nav(self.db) else { return }; let location = location.call_site(); @@ -425,11 +439,6 @@ fn ty_to_text_edit( Some(builder.finish()) } -pub enum RangeLimit { - Fixed(TextRange), - NearestParent(TextSize), -} - // Feature: Inlay Hints // // rust-analyzer shows additional information inline with the source code. @@ -451,7 +460,7 @@ pub enum RangeLimit { pub(crate) fn inlay_hints( db: &RootDatabase, file_id: FileId, - range_limit: Option, + range_limit: Option, config: &InlayHintsConfig, ) -> Vec { let _p = tracing::span!(tracing::Level::INFO, "inlay_hints").entered(); @@ -466,31 +475,13 @@ pub(crate) fn inlay_hints( let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node); match range_limit { - Some(RangeLimit::Fixed(range)) => match file.covering_element(range) { + Some(range) => match file.covering_element(range) { NodeOrToken::Token(_) => return acc, NodeOrToken::Node(n) => n .descendants() .filter(|descendant| range.intersect(descendant.text_range()).is_some()) .for_each(hints), }, - Some(RangeLimit::NearestParent(position)) => { - match file.token_at_offset(position).left_biased() { - Some(token) => { - if let Some(parent_block) = - token.parent_ancestors().find_map(ast::BlockExpr::cast) - { - parent_block.syntax().descendants().for_each(hints) - } else if let Some(parent_item) = - token.parent_ancestors().find_map(ast::Item::cast) - { - parent_item.syntax().descendants().for_each(hints) - } else { - return acc; - } - } - None => return acc, - } - } None => file.descendants().for_each(hints), }; } @@ -498,6 +489,39 @@ pub(crate) fn inlay_hints( acc } +pub(crate) fn inlay_hints_resolve( + db: &RootDatabase, + file_id: FileId, + position: TextSize, + hash: u64, + config: &InlayHintsConfig, +) -> Option { + let _p = tracing::span!(tracing::Level::INFO, "inlay_hints").entered(); + let sema = Semantics::new(db); + let file = sema.parse(file_id); + let file = file.syntax(); + + let scope = sema.scope(file)?; + let famous_defs = FamousDefs(&sema, scope.krate()); + let mut acc = Vec::new(); + + let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node); + match file.token_at_offset(position).left_biased() { + Some(token) => { + if let Some(parent_block) = token.parent_ancestors().find_map(ast::BlockExpr::cast) { + parent_block.syntax().descendants().for_each(hints) + } else if let Some(parent_item) = token.parent_ancestors().find_map(ast::Item::cast) { + parent_item.syntax().descendants().for_each(hints) + } else { + return None; + } + } + None => return None, + } + + acc.into_iter().find(|hint| BuildHasherDefault::::default().hash_one(hint) == hash) +} + fn hints( hints: &mut Vec, famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>, 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 631807d99a7e6..20128a286f26d 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 @@ -147,7 +147,6 @@ pub(super) fn hints( None, ); acc.push(InlayHint { - needs_resolve: label.needs_resolve(), range: expr.syntax().text_range(), pad_left: false, pad_right: false, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index 45b51e3557034..07b9f9cc1fff6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -99,7 +99,6 @@ pub(super) fn hints( None => pat.syntax().text_range(), }; acc.push(InlayHint { - needs_resolve: label.needs_resolve() || text_edit.is_some(), range: match type_ascriptable { Some(Some(t)) => text_range.cover(t.text_range()), _ => text_range, @@ -177,11 +176,7 @@ mod tests { use syntax::{TextRange, TextSize}; use test_utils::extract_annotations; - use crate::{ - fixture, - inlay_hints::{InlayHintsConfig, RangeLimit}, - ClosureReturnTypeHints, - }; + use crate::{fixture, inlay_hints::InlayHintsConfig, ClosureReturnTypeHints}; use crate::inlay_hints::tests::{ check, check_edit, check_no_edit, check_with_config, DISABLED_CONFIG, TEST_CONFIG, @@ -404,7 +399,7 @@ fn main() { .inlay_hints( &InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }, file_id, - Some(RangeLimit::Fixed(TextRange::new(TextSize::from(500), TextSize::from(600)))), + Some(TextRange::new(TextSize::from(500), TextSize::from(600))), ) .unwrap(); let actual = 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 35504ffa7859f..f27390ee898bb 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 @@ -50,7 +50,6 @@ pub(super) fn hints( _ => return, }; acc.push(InlayHint { - needs_resolve: false, range, kind: InlayKind::BindingMode, label: r.into(), @@ -69,7 +68,6 @@ pub(super) fn hints( hir::BindingMode::Ref(Mutability::Shared) => "ref", }; acc.push(InlayHint { - needs_resolve: false, range: pat.syntax().text_range(), kind: InlayKind::BindingMode, label: bm.into(), diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs index b6063978e9233..d86487d4b41b8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs @@ -59,7 +59,6 @@ pub(super) fn hints( } let label = label_of_ty(famous_defs, config, &ty)?; acc.push(InlayHint { - needs_resolve: label.needs_resolve(), range: expr.syntax().text_range(), kind: InlayKind::Chaining, label, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs index 2b68538c198c4..2cefd5acdc2e4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs @@ -109,7 +109,6 @@ pub(super) fn hints( let linked_location = name_range.map(|range| FileRange { file_id, range }); acc.push(InlayHint { - needs_resolve: linked_location.is_some(), range: closing_token.text_range(), kind: InlayKind::ClosingBrace, label: InlayHintLabel::simple(label, None, linked_location), diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs index 2f8b959516df8..f1b524e088095 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs @@ -32,7 +32,6 @@ pub(super) fn hints( let range = closure.syntax().first_token()?.prev_token()?.text_range(); let range = TextRange::new(range.end() - TextSize::from(1), range.end()); acc.push(InlayHint { - needs_resolve: false, range, kind: InlayKind::ClosureCapture, label: InlayHintLabel::from("move"), @@ -45,7 +44,6 @@ pub(super) fn hints( } }; acc.push(InlayHint { - needs_resolve: false, range: move_kw_range, kind: InlayKind::ClosureCapture, label: InlayHintLabel::from("("), @@ -79,7 +77,6 @@ pub(super) fn hints( }), ); acc.push(InlayHint { - needs_resolve: label.needs_resolve(), range: move_kw_range, kind: InlayKind::ClosureCapture, label, @@ -91,7 +88,6 @@ pub(super) fn hints( if idx != last { acc.push(InlayHint { - needs_resolve: false, range: move_kw_range, kind: InlayKind::ClosureCapture, label: InlayHintLabel::from(", "), @@ -103,7 +99,6 @@ pub(super) fn hints( } } acc.push(InlayHint { - needs_resolve: false, range: move_kw_range, kind: InlayKind::ClosureCapture, label: InlayHintLabel::from(")"), diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs index 204967cd7ca89..3b41db0f13d0a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs @@ -64,7 +64,6 @@ pub(super) fn hints( }; acc.push(InlayHint { - needs_resolve: label.needs_resolve() || text_edit.is_some(), range: param_list.syntax().text_range(), kind: InlayKind::Type, label, 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 06cce147d2a16..202954100fb97 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 @@ -79,7 +79,6 @@ fn variant_hints( None, ); acc.push(InlayHint { - needs_resolve: label.needs_resolve(), range: match eq_token { Some(t) => range.cover(t.text_range()), _ => range, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs index 6e5f23bed09fb..d3666754e2be9 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs @@ -22,7 +22,6 @@ pub(super) fn hints( } let mk_lt_hint = |t: SyntaxToken, label: String| InlayHint { - needs_resolve: false, range: t.text_range(), kind: InlayKind::Lifetime, label: label.into(), @@ -184,7 +183,6 @@ pub(super) fn hints( let angle_tok = gpl.l_angle_token()?; let is_empty = gpl.generic_params().next().is_none(); acc.push(InlayHint { - needs_resolve: false, range: angle_tok.text_range(), kind: InlayKind::Lifetime, label: format!( @@ -200,7 +198,6 @@ pub(super) fn hints( }); } (None, allocated_lifetimes) => acc.push(InlayHint { - needs_resolve: false, range: func.name()?.syntax().text_range(), kind: InlayKind::GenericParamList, label: format!("<{}>", allocated_lifetimes.iter().format(", "),).into(), diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs index 5ba4e514e1f08..31f0c79037453 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs @@ -105,7 +105,6 @@ pub(super) fn hints( pad_left: true, pad_right: true, kind: InlayKind::Drop, - needs_resolve: label.needs_resolve(), label, text_edit: None, }) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs index f18e6421cbcbd..42223ddf580e3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs @@ -31,7 +31,6 @@ pub(super) fn hints( if ty.lifetime().is_none() { let t = ty.amp_token()?; acc.push(InlayHint { - needs_resolve: false, range: t.text_range(), kind: InlayKind::Lifetime, label: "'static".into(), 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 418fc002a8beb..96e845b2f3235 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 @@ -57,7 +57,6 @@ pub(super) fn hints( let label = InlayHintLabel::simple(format!("{param_name}{colon}"), None, linked_location); InlayHint { - needs_resolve: label.needs_resolve(), range, kind: InlayKind::Parameter, label, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs index c4b0c199fc239..bfb92838857ca 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs @@ -30,7 +30,6 @@ fn inlay_hint(token: SyntaxToken) -> InlayHint { kind: crate::InlayKind::RangeExclusive, label: crate::InlayHintLabel::from("<"), text_edit: None, - needs_resolve: false, } } diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 6955e14a10a14..ad48d803895f9 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -58,8 +58,6 @@ mod view_item_tree; mod view_memory_layout; mod view_mir; -use std::ffi::OsStr; - use cfg::CfgOptions; use fetch_crates::CrateInfo; use hir::ChangeWithProcMacros; @@ -90,7 +88,7 @@ pub use crate::{ inlay_hints::{ AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayHintPosition, - InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints, RangeLimit, + InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints, }, join_lines::JoinLinesConfig, markup::Markup, @@ -121,8 +119,8 @@ pub use ide_completion::{ }; pub use ide_db::{ base_db::{ - Cancelled, CrateGraph, CrateId, Edition, FileChange, FileId, FilePosition, FileRange, - SourceRoot, SourceRootId, + Cancelled, CrateGraph, CrateId, FileChange, FileId, FilePosition, FileRange, SourceRoot, + SourceRootId, }, documentation::Documentation, label::Label, @@ -137,6 +135,7 @@ pub use ide_diagnostics::{ Diagnostic, DiagnosticCode, DiagnosticsConfig, ExprFillDefaultMode, Severity, }; pub use ide_ssr::SsrError; +pub use span::Edition; pub use syntax::{TextRange, TextSize}; pub use text_edit::{Indel, TextEdit}; @@ -354,6 +353,10 @@ impl Analysis { self.with_db(|db| test_explorer::discover_tests_in_crate(db, crate_id)) } + pub fn discover_tests_in_file(&self, file_id: FileId) -> Cancellable> { + self.with_db(|db| test_explorer::discover_tests_in_file(db, file_id)) + } + /// Renders the crate graph to GraphViz "dot" syntax. pub fn view_crate_graph(&self, full: bool) -> Cancellable> { self.with_db(|db| view_crate_graph::view_crate_graph(db, full)) @@ -415,10 +418,19 @@ impl Analysis { &self, config: &InlayHintsConfig, file_id: FileId, - range: Option, + range: Option, ) -> Cancellable> { self.with_db(|db| inlay_hints::inlay_hints(db, file_id, range, config)) } + pub fn inlay_hints_resolve( + &self, + config: &InlayHintsConfig, + file_id: FileId, + position: TextSize, + hash: u64, + ) -> Cancellable> { + self.with_db(|db| inlay_hints::inlay_hints_resolve(db, file_id, position, hash, config)) + } /// Returns the set of folding ranges. pub fn folding_ranges(&self, file_id: FileId) -> Cancellable> { @@ -502,8 +514,8 @@ impl Analysis { pub fn external_docs( &self, position: FilePosition, - target_dir: Option<&OsStr>, - sysroot: Option<&OsStr>, + target_dir: Option<&str>, + sysroot: Option<&str>, ) -> Cancellable { self.with_db(|db| { doc_links::external_docs(db, position, target_dir, sysroot).unwrap_or_default() 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 fe063081f7999..3fef16df25e36 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -167,6 +167,7 @@ impl StaticIndex<'_> { keywords: true, format: crate::HoverDocFormat::Markdown, max_trait_assoc_items_count: None, + max_struct_field_count: None, }; let tokens = tokens.filter(|token| { matches!( diff --git a/src/tools/rust-analyzer/crates/ide/src/status.rs b/src/tools/rust-analyzer/crates/ide/src/status.rs index c3d85e38936d9..8e7767c8e5d79 100644 --- a/src/tools/rust-analyzer/crates/ide/src/status.rs +++ b/src/tools/rust-analyzer/crates/ide/src/status.rs @@ -10,7 +10,7 @@ use ide_db::{ debug::{DebugQueryTable, TableEntry}, Query, QueryTable, }, - CrateData, FileId, FileTextQuery, ParseQuery, SourceDatabase, SourceRootId, + CompressedFileTextQuery, CrateData, FileId, ParseQuery, SourceDatabase, SourceRootId, }, symbol_index::ModuleSymbolsQuery, }; @@ -38,7 +38,7 @@ use triomphe::Arc; pub(crate) fn status(db: &RootDatabase, file_id: Option) -> String { let mut buf = String::new(); - format_to!(buf, "{}\n", collect_query(FileTextQuery.in_db(db))); + format_to!(buf, "{}\n", collect_query(CompressedFileTextQuery.in_db(db))); format_to!(buf, "{}\n", collect_query(ParseQuery.in_db(db))); format_to!(buf, "{}\n", collect_query(ParseMacroExpansionQuery.in_db(db))); format_to!(buf, "{}\n", collect_query(LibrarySymbolsQuery.in_db(db))); @@ -160,7 +160,7 @@ impl QueryCollect for ParseMacroExpansionQuery { type Collector = SyntaxTreeStats; } -impl QueryCollect for FileTextQuery { +impl QueryCollect for CompressedFileTextQuery { type Collector = FilesStats; } @@ -188,8 +188,8 @@ impl fmt::Display for FilesStats { } } -impl StatCollect> for FilesStats { - fn collect_entry(&mut self, _: FileId, value: Option>) { +impl StatCollect> for FilesStats { + fn collect_entry(&mut self, _: FileId, value: Option>) { self.total += 1; self.size += value.unwrap().len(); } 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 96c7c47559421..e7346cbb9925c 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 @@ -178,6 +178,23 @@ fn keyword( T![do] | T![yeet] if parent_matches::(&token) => h | HlMod::ControlFlow, T![for] if parent_matches::(&token) => h | HlMod::ControlFlow, T![unsafe] => h | HlMod::Unsafe, + T![const] + if token.parent().map_or(false, |it| { + matches!( + it.kind(), + SyntaxKind::CONST + | SyntaxKind::FN + | SyntaxKind::IMPL + | SyntaxKind::BLOCK_EXPR + | SyntaxKind::CLOSURE_EXPR + | SyntaxKind::FN_PTR_TYPE + | SyntaxKind::TYPE_BOUND + | SyntaxKind::CONST_BLOCK_PAT + ) + }) => + { + h | HlMod::Const + } T![true] | T![false] => HlTag::BoolLiteral.into(), // crate is handled just as a token if it's in an `extern crate` T![crate] if parent_matches::(&token) => h, @@ -377,14 +394,17 @@ pub(super) fn highlight_def( if let Some(item) = func.as_assoc_item(db) { h |= HlMod::Associated; match func.self_param(db) { - Some(sp) => match sp.access(db) { - hir::Access::Exclusive => { - h |= HlMod::Mutable; - h |= HlMod::Reference; + Some(sp) => { + h.tag = HlTag::Symbol(SymbolKind::Method); + match sp.access(db) { + hir::Access::Exclusive => { + h |= HlMod::Mutable; + h |= HlMod::Reference; + } + hir::Access::Shared => h |= HlMod::Reference, + hir::Access::Owned => h |= HlMod::Consuming, } - hir::Access::Shared => h |= HlMod::Reference, - hir::Access::Owned => h |= HlMod::Consuming, - }, + } None => h |= HlMod::Static, } @@ -406,6 +426,9 @@ pub(super) fn highlight_def( if func.is_async(db) { h |= HlMod::Async; } + if func.is_const(db) { + h |= HlMod::Const; + } h } @@ -420,10 +443,11 @@ pub(super) fn highlight_def( } Definition::Variant(_) => Highlight::new(HlTag::Symbol(SymbolKind::Variant)), Definition::Const(konst) => { - let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Const)); + let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Const)) | HlMod::Const; if let Some(item) = konst.as_assoc_item(db) { h |= HlMod::Associated; + h |= HlMod::Static; match item.container(db) { hir::AssocItemContainer::Impl(i) => { if i.trait_(db).is_some() { @@ -445,6 +469,7 @@ pub(super) fn highlight_def( if let Some(item) = type_.as_assoc_item(db) { h |= HlMod::Associated; + h |= HlMod::Static; match item.container(db) { hir::AssocItemContainer::Impl(i) => { if i.trait_(db).is_some() { @@ -474,7 +499,7 @@ pub(super) fn highlight_def( Definition::GenericParam(it) => match it { hir::GenericParam::TypeParam(_) => Highlight::new(HlTag::Symbol(SymbolKind::TypeParam)), hir::GenericParam::ConstParam(_) => { - Highlight::new(HlTag::Symbol(SymbolKind::ConstParam)) + Highlight::new(HlTag::Symbol(SymbolKind::ConstParam)) | HlMod::Const } hir::GenericParam::LifetimeParam(_) => { Highlight::new(HlTag::Symbol(SymbolKind::LifetimeParam)) @@ -550,8 +575,7 @@ fn highlight_method_call( ) -> Option { let func = sema.resolve_method_call(method_call)?; - let mut h = SymbolKind::Function.into(); - h |= HlMod::Associated; + let mut h = SymbolKind::Method.into(); if func.is_unsafe_to_call(sema.db) || sema.is_unsafe_method_call(method_call) { h |= HlMod::Unsafe; @@ -647,7 +671,7 @@ fn highlight_name_ref_by_syntax( match parent.kind() { METHOD_CALL_EXPR => ast::MethodCallExpr::cast(parent) .and_then(|it| highlight_method_call(sema, krate, &it)) - .unwrap_or_else(|| SymbolKind::Function.into()), + .unwrap_or_else(|| SymbolKind::Method.into()), FIELD_EXPR => { let h = HlTag::Symbol(SymbolKind::Field); let is_union = ast::FieldExpr::cast(parent) diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs index a6dca0541e5f0..e754b702deed1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs @@ -109,6 +109,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs index 5163a0de41770..5c7a463ccdc95 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs @@ -46,7 +46,7 @@ pub enum HlTag { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] #[repr(u8)] pub enum HlMod { - /// Used for items in traits and impls. + /// Used for associated items. Associated = 0, /// Used with keywords like `async` and `await`. Async, @@ -54,6 +54,8 @@ pub enum HlMod { Attribute, /// Callable item or value. Callable, + /// Constant operation. + Const, /// Value that is being consumed in a function call Consuming, /// Used with keywords like `if` and `break`. @@ -82,7 +84,7 @@ pub enum HlMod { Public, /// Immutable reference. Reference, - /// Used for associated functions. + /// Used for associated items, except Methods. (Some languages call these static members) Static, /// Used for items in traits and trait impls. Trait, @@ -147,6 +149,7 @@ impl HlTag { SymbolKind::LifetimeParam => "lifetime", SymbolKind::Local => "variable", SymbolKind::Macro => "macro", + SymbolKind::Method => "method", SymbolKind::ProcMacro => "proc_macro", SymbolKind::Module => "module", SymbolKind::SelfParam => "self_keyword", @@ -211,6 +214,7 @@ impl HlMod { HlMod::Async, HlMod::Attribute, HlMod::Callable, + HlMod::Const, HlMod::Consuming, HlMod::ControlFlow, HlMod::CrateRoot, @@ -237,6 +241,7 @@ impl HlMod { HlMod::Attribute => "attribute", HlMod::Callable => "callable", HlMod::Consuming => "consuming", + HlMod::Const => "const", HlMod::ControlFlow => "control", HlMod::CrateRoot => "crate_root", HlMod::DefaultLibrary => "default_library", diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html index 6994cb3d5c542..9c7f03fc1587e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html @@ -40,6 +40,7 @@ .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -50,15 +51,15 @@ impl foo { pub fn is_static() {} - pub fn is_not_static(&self) {} + pub fn is_not_static(&self) {} } trait t { fn t_is_static() {} - fn t_is_not_static(&self) {} + fn t_is_not_static(&self) {} } impl t for foo { pub fn is_static() {} - pub fn is_not_static(&self) {} + pub fn is_not_static(&self) {} } \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html index dc2d103b5815f..de902b5137d0b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html @@ -40,6 +40,7 @@ .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -47,7 +48,7 @@
#[allow(dead_code)]
 #[rustfmt::skip]
 #[proc_macros::identity]
-#[derive(Copy)]
+#[derive(Default)]
 /// This is a doc comment
 // This is a normal comment
 /// This is a doc comment
@@ -57,4 +58,7 @@
 // This is another normal comment
 #[derive(Copy, Unresolved)]
 // The reason for these being here is to test AttrIds
-struct Foo;
\ No newline at end of file +enum Foo { + #[default] + Bar +} \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html index 093cc2358a676..eed3968a9068d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html @@ -40,6 +40,7 @@ .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -58,7 +59,7 @@ foo!(Bar); fn func() { mod inner { - struct Innerest<const C: usize> { field: [(); {C}] } + struct Innerest<const C: usize> { field: [(); {C}] } } } } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_const.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_const.html new file mode 100644 index 0000000000000..cd6ffc2c3ae3b --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_const.html @@ -0,0 +1,83 @@ + + +
macro_rules! id {
+    ($($tt:tt)*) => {
+        $($tt)*
+    };
+}
+const CONST_ITEM: *const () = &raw const ();
+const fn const_fn<const CONST_PARAM: ()>(const {}: const fn()) where (): const ConstTrait {
+    CONST_ITEM;
+    CONST_PARAM;
+    const {
+        const || {}
+    }
+    id!(
+        CONST_ITEM;
+        CONST_PARAM;
+        const {
+            const || {}
+        };
+        &raw const ();
+        const
+    );
+}
+trait ConstTrait {
+    const ASSOC_CONST: () = ();
+    const fn assoc_const_fn() {}
+}
+impl const ConstTrait for () {
+    const ASSOC_CONST: () = ();
+    const fn assoc_const_fn() {}
+}
+
+macro_rules! unsafe_deref {
+    () => {
+        *(&() as *const ())
+    };
+}
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html index 154b823fffb16..63e1560b92107 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html @@ -40,6 +40,7 @@ .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -47,7 +48,7 @@
extern crate foo;
 use core::iter;
 
-pub const NINETY_TWO: u8 = 92;
+pub const NINETY_TWO: u8 = 92;
 
 use foo as foooo;
 
@@ -56,13 +57,13 @@
 }
 
 mod bar {
-    pub(in super) const FORTY_TWO: u8 = 42;
+    pub(in super) const FORTY_TWO: u8 = 42;
 
     mod baz {
-        use super::super::NINETY_TWO;
+        use super::super::NINETY_TWO;
         use crate::foooo::Point;
 
-        pub(in super::super) const TWENTY_NINE: u8 = 29;
+        pub(in super::super) const TWENTY_NINE: u8 = 29;
     }
 }
 
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html index 58613bf151050..366895ce7ec70 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html @@ -40,6 +40,7 @@ .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -48,5 +49,5 @@ fn main() { let foo = Some(92); - let nums = iter::repeat(foo.unwrap()); + let nums = iter::repeat(foo.unwrap()); } \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index 34274932afc17..b2ca6e1ca4b66 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html @@ -40,6 +40,7 @@ .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -71,7 +72,7 @@ // KILLER WHALE /// Ishmael."; /// ``` - pub const bar: bool = true; + pub const bar: bool = true; /// Constructs a new `Foo`. /// @@ -81,7 +82,7 @@ /// # #![allow(unused_mut)] /// let mut foo: Foo = Foo::new(); /// ``` - pub const fn new() -> Foo { + pub const fn new() -> Foo { Foo { bar: true } } @@ -109,25 +110,25 @@ /// ``` /// /// ```rust,no_run - /// let foobar = Foo::new().bar(); + /// let foobar = Foo::new().bar(); /// ``` /// /// ~~~rust,no_run /// // code block with tilde. - /// let foobar = Foo::new().bar(); + /// let foobar = Foo::new().bar(); /// ~~~ /// /// ``` /// // functions - /// fn foo<T, const X: usize>(arg: i32) { - /// let x: T = X; + /// fn foo<T, const X: usize>(arg: i32) { + /// let x: T = X; /// } /// ``` /// /// ```sh /// echo 1 /// ``` - pub fn foo(&self) -> bool { + pub fn foo(&self) -> bool { true } } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html index 729e4791f557a..129b287e52f2d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html @@ -40,6 +40,7 @@ .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html index 066fcfb1dfe6f..7ba1194d675f5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html @@ -40,6 +40,7 @@ .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -63,25 +64,25 @@ } trait Bar { - fn bar(&self) -> i32; + fn bar(&self) -> i32; } impl Bar for Foo { - fn bar(&self) -> i32 { + fn bar(&self) -> i32 { self.x } } impl Foo { - fn baz(mut self, f: Foo) -> i32 { - f.baz(self) + fn baz(mut self, f: Foo) -> i32 { + f.baz(self) } - fn qux(&mut self) { + fn qux(&mut self) { self.x = 0; } - fn quop(&self) -> i32 { + fn quop(&self) -> i32 { self.x } } @@ -94,15 +95,15 @@ } impl FooCopy { - fn baz(self, f: FooCopy) -> u32 { - f.baz(self) + fn baz(self, f: FooCopy) -> u32 { + f.baz(self) } - fn qux(&mut self) { + fn qux(&mut self) { self.x = 0; } - fn quop(&self) -> u32 { + fn quop(&self) -> u32 { self.x } } @@ -119,9 +120,9 @@ loop {} } -fn const_param<const FOO: usize>() -> usize { - const_param::<{ FOO }>(); - FOO +fn const_param<const FOO: usize>() -> usize { + const_param::<{ FOO }>(); + FOO } use ops::Fn; @@ -148,17 +149,17 @@ let mut foo = Foo { x, y: x }; let foo2 = Foo { x, y: x }; - foo.quop(); - foo.qux(); - foo.baz(foo2); + foo.quop(); + foo.qux(); + foo.baz(foo2); let mut copy = FooCopy { x }; - copy.quop(); - copy.qux(); - copy.baz(copy); + copy.quop(); + copy.qux(); + copy.baz(copy); let a = |x| x; - let bar = Foo::baz; + let bar = Foo::baz; let baz = (-42,); let baz = -baz.0; @@ -178,7 +179,7 @@ use Option::*; impl<T> Option<T> { - fn and<U>(self, other: Option<U>) -> Option<(T, U)> { + fn and<U>(self, other: Option<U>) -> Option<(T, U)> { match other { None => unimplemented!(), Nope => Nope, @@ -200,12 +201,12 @@ fn use_foo_items() { let bob = foo::Person { name: "Bob", - age: foo::consts::NUMBER, + age: foo::consts::NUMBER, }; let control_flow = foo::identity(foo::ControlFlow::Continue); - if control_flow.should_die() { + if control_flow.should_die() { foo::die!(); } } @@ -213,23 +214,23 @@ pub enum Bool { True, False } impl Bool { - pub const fn to_primitive(self) -> bool { + pub const fn to_primitive(self) -> bool { true } } -const USAGE_OF_BOOL:bool = Bool::True.to_primitive(); +const USAGE_OF_BOOL:bool = Bool::True.to_primitive(); trait Baz { - type Qux; + type Qux; } fn baz<T>(t: T) where T: Baz, - <T as Baz>::Qux: Bar {} + <T as Baz>::Qux: Bar {} fn gp_shadows_trait<Baz: Bar>() { - Baz::bar; + Baz::bar; } \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html index 58a147dd80a4f..6c3fbcfcf41e2 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html @@ -40,6 +40,7 @@ .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html index 22ae5c82a4b55..7064a877070c7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html @@ -40,6 +40,7 @@ .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html index af7799965937a..8428b81580036 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html @@ -40,6 +40,7 @@ .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html index 32ac6a94d8689..573b3d4bd5279 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html @@ -40,6 +40,7 @@ .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html index ef8a48ca1c1d3..947d1bf1e3570 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html @@ -40,6 +40,7 @@ .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html index a2ded15fd1baf..0fe2b6f274d03 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html @@ -40,6 +40,7 @@ .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html index d123ee049765e..c60b6ab27bba5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html @@ -40,6 +40,7 @@ .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html index 4429e5d933aca..5d51b1497895c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html @@ -40,6 +40,7 @@ .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index b6458fa7ca098..7a07d17b2718d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -40,6 +40,7 @@ .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -171,9 +172,9 @@ in(reg) i, ); - const CONSTANT: () = (): + const CONSTANT: () = (): let mut m = (); format_args!(concat!("{}"), "{}"); - format_args!("{} {} {} {} {} {} {backslash} {CONSTANT} {m}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash); + format_args!("{} {} {} {} {} {} {backslash} {CONSTANT} {m}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash); reuse_twice!("{backslash}"); } \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html index 49b588baa58e1..15d6be6334c6f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html @@ -40,6 +40,7 @@ .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -65,7 +66,7 @@ struct Struct { field: i32 } impl Struct { - unsafe fn unsafe_method(&self) {} + unsafe fn unsafe_method(&self) {} } #[repr(packed)] @@ -80,11 +81,11 @@ fn unsafe_trait_bound<T: UnsafeTrait>(_: T) {} trait DoTheAutoref { - fn calls_autoref(&self); + fn calls_autoref(&self); } impl DoTheAutoref for u16 { - fn calls_autoref(&self) {} + fn calls_autoref(&self) {} } fn main() { @@ -106,7 +107,7 @@ Union { b: 0 } => (), Union { a } => (), } - Struct { field: 0 }.unsafe_method(); + Struct { field: 0 }.unsafe_method(); // unsafe deref *x; @@ -123,6 +124,6 @@ let Packed { a: ref _a } = packed; // unsafe auto ref of packed field - packed.a.calls_autoref(); + packed.a.calls_autoref(); } } \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs index 6fed7d783e81e..c2990fd76eaca 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs @@ -22,11 +22,11 @@ fn attributes() { check_highlighting( r#" //- proc_macros: identity -//- minicore: derive, copy +//- minicore: derive, copy, default #[allow(dead_code)] #[rustfmt::skip] #[proc_macros::identity] -#[derive(Copy)] +#[derive(Default)] /// This is a doc comment // This is a normal comment /// This is a doc comment @@ -36,7 +36,10 @@ fn attributes() { // This is another normal comment #[derive(Copy, Unresolved)] // The reason for these being here is to test AttrIds -struct Foo; +enum Foo { + #[default] + Bar +} "#, expect_file!["./test_data/highlight_attributes.html"], false, @@ -629,6 +632,52 @@ fn main() { ); } +#[test] +fn test_const_highlighting() { + check_highlighting( + r#" +macro_rules! id { + ($($tt:tt)*) => { + $($tt)* + }; +} +const CONST_ITEM: *const () = &raw const (); +const fn const_fn(const {}: const fn()) where (): const ConstTrait { + CONST_ITEM; + CONST_PARAM; + const { + const || {} + } + id!( + CONST_ITEM; + CONST_PARAM; + const { + const || {} + }; + &raw const (); + const + ); +} +trait ConstTrait { + const ASSOC_CONST: () = (); + const fn assoc_const_fn() {} +} +impl const ConstTrait for () { + const ASSOC_CONST: () = (); + const fn assoc_const_fn() {} +} + +macro_rules! unsafe_deref { + () => { + *(&() as *const ()) + }; +} +"#, + expect_file!["./test_data/highlight_const.html"], + false, + ); +} + #[test] fn test_highlight_doc_comment() { check_highlighting( @@ -1173,8 +1222,27 @@ fn benchmark_syntax_highlighting_parser() { .highlight(HL_CONFIG, file_id) .unwrap() .iter() - .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function)) + .filter(|it| { + matches!(it.highlight.tag, HlTag::Symbol(SymbolKind::Function | SymbolKind::Method)) + }) .count() }; assert_eq!(hash, 1169); } + +#[test] +fn highlight_trait_with_lifetimes_regression_16958() { + let (analysis, file_id) = fixture::file( + r#" +pub trait Deserialize<'de> { + fn deserialize(); +} + +fn f<'de, T: Deserialize<'de>>() { + T::deserialize(); +} +"# + .trim(), + ); + let _ = analysis.highlight(HL_CONFIG, file_id).unwrap(); +} diff --git a/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs b/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs index ca4713997030e..99e2430860794 100644 --- a/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs +++ b/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs @@ -7,7 +7,7 @@ use ide_db::{ }; use syntax::TextRange; -use crate::{navigation_target::ToNav, runnables::runnable_fn, Runnable, TryToNav}; +use crate::{runnables::runnable_fn, NavigationTarget, Runnable, TryToNav}; #[derive(Debug)] pub enum TestItemKind { @@ -56,7 +56,12 @@ fn find_crate_by_id(crate_graph: &CrateGraph, crate_id: &str) -> Option }) } -fn discover_tests_in_module(db: &RootDatabase, module: Module, prefix_id: String) -> Vec { +fn discover_tests_in_module( + db: &RootDatabase, + module: Module, + prefix_id: String, + only_in_this_file: bool, +) -> Vec { let sema = Semantics::new(db); let mut r = vec![]; @@ -64,9 +69,9 @@ fn discover_tests_in_module(db: &RootDatabase, module: Module, prefix_id: String let module_name = c.name(db).as_ref().and_then(|n| n.as_str()).unwrap_or("[mod without name]").to_owned(); let module_id = format!("{prefix_id}::{module_name}"); - let module_children = discover_tests_in_module(db, c, module_id.clone()); + let module_children = discover_tests_in_module(db, c, module_id.clone(), only_in_this_file); if !module_children.is_empty() { - let nav = c.to_nav(db).call_site; + let nav = NavigationTarget::from_module_to_decl(sema.db, c).call_site; r.push(TestItem { id: module_id, kind: TestItemKind::Module, @@ -76,7 +81,9 @@ fn discover_tests_in_module(db: &RootDatabase, module: Module, prefix_id: String text_range: Some(nav.focus_or_full_range()), runnable: None, }); - r.extend(module_children); + if !only_in_this_file || c.is_inline(db) { + r.extend(module_children); + } } } for def in module.declarations(db) { @@ -112,6 +119,55 @@ pub(crate) fn discover_tests_in_crate_by_test_id( discover_tests_in_crate(db, crate_id) } +pub(crate) fn discover_tests_in_file(db: &RootDatabase, file_id: FileId) -> Vec { + let sema = Semantics::new(db); + + let Some(module) = sema.file_to_module_def(file_id) else { return vec![] }; + let Some((mut tests, id)) = find_module_id_and_test_parents(&sema, module) else { + return vec![]; + }; + tests.extend(discover_tests_in_module(db, module, id, true)); + tests +} + +fn find_module_id_and_test_parents( + sema: &Semantics<'_, RootDatabase>, + module: Module, +) -> Option<(Vec, String)> { + let Some(parent) = module.parent(sema.db) else { + let name = module.krate().display_name(sema.db)?.to_string(); + return Some(( + vec![TestItem { + id: name.clone(), + kind: TestItemKind::Crate(module.krate().into()), + label: name.clone(), + parent: None, + file: None, + text_range: None, + runnable: None, + }], + name, + )); + }; + let (mut r, mut id) = find_module_id_and_test_parents(sema, parent)?; + let parent = Some(id.clone()); + id += "::"; + let module_name = &module.name(sema.db); + let module_name = module_name.as_ref().and_then(|n| n.as_str()).unwrap_or("[mod without name]"); + id += module_name; + let nav = NavigationTarget::from_module_to_decl(sema.db, module).call_site; + r.push(TestItem { + id: id.clone(), + kind: TestItemKind::Module, + label: module_name.to_owned(), + parent, + file: Some(nav.file_id), + text_range: Some(nav.focus_or_full_range()), + runnable: None, + }); + Some((r, id)) +} + pub(crate) fn discover_tests_in_crate(db: &RootDatabase, crate_id: CrateId) -> Vec { let crate_graph = db.crate_graph(); if !crate_graph[crate_id].origin.is_local() { @@ -133,6 +189,6 @@ pub(crate) fn discover_tests_in_crate(db: &RootDatabase, crate_id: CrateId) -> V text_range: None, runnable: None, }]; - r.extend(discover_tests_in_module(db, module, crate_test_id)); + r.extend(discover_tests_in_module(db, module, crate_test_id, false)); r } diff --git a/src/tools/rust-analyzer/crates/ide/src/typing.rs b/src/tools/rust-analyzer/crates/ide/src/typing.rs index e87fc89fea2a8..d3eee0e02e483 100644 --- a/src/tools/rust-analyzer/crates/ide/src/typing.rs +++ b/src/tools/rust-analyzer/crates/ide/src/typing.rs @@ -175,9 +175,21 @@ fn on_opening_bracket_typed( } } - // If it's a statement in a block, we don't know how many statements should be included - if ast::ExprStmt::can_cast(expr.syntax().parent()?.kind()) { - return None; + if let Some(parent) = expr.syntax().parent().and_then(ast::Expr::cast) { + let mut node = expr.syntax().clone(); + let all_prev_sib_attr = loop { + match node.prev_sibling() { + Some(sib) if sib.kind().is_trivia() || sib.kind() == SyntaxKind::ATTR => { + node = sib + } + Some(_) => break false, + None => break true, + }; + }; + + if all_prev_sib_attr { + expr = parent; + } } // Insert the closing bracket right after the expression. @@ -824,6 +836,21 @@ fn f() { 0 => {()}, 1 => (), } +} + "#, + ); + type_char( + '{', + r#" +fn main() { + #[allow(unreachable_code)] + $0g(); +} + "#, + r#" +fn main() { + #[allow(unreachable_code)] + {g()}; } "#, ); diff --git a/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml b/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml index 05412e176b65b..48e84a7b25ebf 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml +++ b/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml @@ -20,6 +20,7 @@ tracing.workspace = true hir-expand.workspace = true ide-db.workspace = true +paths.workspace = true proc-macro-api.workspace = true project-model.workspace = true span.workspace = true 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 fe680e47fef8f..79d6fe36b5618 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -38,7 +38,7 @@ pub fn load_workspace_at( load_config: &LoadCargoConfig, progress: &dyn Fn(String), ) -> anyhow::Result<(RootDatabase, vfs::Vfs, Option)> { - let root = AbsPathBuf::assert(std::env::current_dir()?.join(root)); + 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)?; @@ -387,7 +387,7 @@ fn expander_to_proc_macro( let name = From::from(expander.name()); let kind = match expander.kind() { proc_macro_api::ProcMacroKind::CustomDerive => ProcMacroKind::CustomDerive, - proc_macro_api::ProcMacroKind::FuncLike => ProcMacroKind::FuncLike, + proc_macro_api::ProcMacroKind::Bang => ProcMacroKind::Bang, proc_macro_api::ProcMacroKind::Attr => ProcMacroKind::Attr, }; let disabled = ignored_macros.iter().any(|replace| **replace == name); diff --git a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs index ac7f07117845e..4d5531ae307fb 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs @@ -23,7 +23,11 @@ fn benchmark_parse_macro_rules() { let _pt = bench("mbe parse macro rules"); rules .values() - .map(|it| DeclarativeMacro::parse_macro_rules(it, true, true).rules.len()) + .map(|it| { + DeclarativeMacro::parse_macro_rules(it, |_| span::Edition::CURRENT, true) + .rules + .len() + }) .sum() }; assert_eq!(hash, 1144); @@ -51,10 +55,12 @@ fn benchmark_expand_macro_rules() { assert_eq!(hash, 69413); } -fn macro_rules_fixtures() -> FxHashMap> { +fn macro_rules_fixtures() -> FxHashMap { macro_rules_fixtures_tt() .into_iter() - .map(|(id, tt)| (id, DeclarativeMacro::parse_macro_rules(&tt, true, true))) + .map(|(id, tt)| { + (id, DeclarativeMacro::parse_macro_rules(&tt, |_| span::Edition::CURRENT, true)) + }) .collect() } @@ -80,7 +86,7 @@ fn macro_rules_fixtures_tt() -> FxHashMap> { /// Generate random invocation fixtures from rules fn invocation_fixtures( - rules: &FxHashMap>, + rules: &FxHashMap, ) -> Vec<(String, tt::Subtree)> { let mut seed = 123456789; let mut res = Vec::new(); @@ -128,11 +134,7 @@ fn invocation_fixtures( } return res; - fn collect_from_op( - op: &Op, - token_trees: &mut Vec>, - seed: &mut usize, - ) { + fn collect_from_op(op: &Op, token_trees: &mut Vec>, seed: &mut usize) { return match op { Op::Var { kind, .. } => match kind.as_ref() { Some(MetaVarKind::Ident) => token_trees.push(make_ident("foo")), diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander.rs b/src/tools/rust-analyzer/crates/mbe/src/expander.rs index 9366048fd9558..2f2c0aa6ff595 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander.rs @@ -6,22 +6,21 @@ mod matcher; mod transcriber; use rustc_hash::FxHashMap; +use span::Span; use syntax::SmolStr; -use tt::Span; use crate::{parser::MetaVarKind, ExpandError, ExpandResult}; -pub(crate) fn expand_rules( - rules: &[crate::Rule], - input: &tt::Subtree, - marker: impl Fn(&mut S) + Copy, - is_2021: bool, +pub(crate) fn expand_rules( + rules: &[crate::Rule], + input: &tt::Subtree, + marker: impl Fn(&mut Span) + Copy, new_meta_vars: bool, - call_site: S, -) -> ExpandResult> { - let mut match_: Option<(matcher::Match, &crate::Rule)> = None; + call_site: Span, +) -> ExpandResult> { + let mut match_: Option<(matcher::Match, &crate::Rule)> = None; for rule in rules { - let new_match = matcher::match_(&rule.lhs, input, is_2021); + let new_match = matcher::match_(&rule.lhs, input); if new_match.err.is_none() { // If we find a rule that applies without errors, we're done. @@ -110,30 +109,24 @@ pub(crate) fn expand_rules( /// In other words, `Bindings` is a *multi* mapping from `SmolStr` to /// `tt::TokenTree`, where the index to select a particular `TokenTree` among /// many is not a plain `usize`, but a `&[usize]`. -#[derive(Debug, Clone, PartialEq, Eq)] -struct Bindings { - inner: FxHashMap>, -} - -impl Default for Bindings { - fn default() -> Self { - Self { inner: Default::default() } - } +#[derive(Debug, Default, Clone, PartialEq, Eq)] +struct Bindings { + inner: FxHashMap, } #[derive(Debug, Clone, PartialEq, Eq)] -enum Binding { - Fragment(Fragment), - Nested(Vec>), +enum Binding { + Fragment(Fragment), + Nested(Vec), Empty, Missing(MetaVarKind), } #[derive(Debug, Clone, PartialEq, Eq)] -enum Fragment { +enum Fragment { Empty, /// token fragments are just copy-pasted into the output - Tokens(tt::TokenTree), + 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. @@ -141,7 +134,7 @@ enum Fragment { /// 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), + Expr(tt::Subtree), /// 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. @@ -151,5 +144,5 @@ 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::Subtree), } 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 eea92cfba4c20..3170834d54ffb 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs @@ -62,8 +62,9 @@ use std::rc::Rc; use smallvec::{smallvec, SmallVec}; +use span::Span; use syntax::SmolStr; -use tt::{DelimSpan, Span}; +use tt::DelimSpan; use crate::{ expander::{Binding, Bindings, ExpandResult, Fragment}, @@ -72,7 +73,7 @@ use crate::{ ExpandError, MetaTemplate, ValueResult, }; -impl Bindings { +impl Bindings { fn push_optional(&mut self, name: &SmolStr) { self.inner.insert(name.clone(), Binding::Fragment(Fragment::Empty)); } @@ -81,14 +82,14 @@ impl Bindings { self.inner.insert(name.clone(), Binding::Empty); } - fn bindings(&self) -> impl Iterator> { + fn bindings(&self) -> impl Iterator { self.inner.values() } } -#[derive(Clone, Debug, PartialEq, Eq)] -pub(super) struct Match { - pub(super) bindings: Bindings, +#[derive(Clone, Default, Debug, PartialEq, Eq)] +pub(super) struct Match { + pub(super) bindings: Bindings, /// We currently just keep the first error and count the rest to compare matches. pub(super) err: Option, pub(super) err_count: usize, @@ -98,19 +99,7 @@ pub(super) struct Match { pub(super) bound_count: usize, } -impl Default for Match { - fn default() -> Self { - Self { - bindings: Default::default(), - err: Default::default(), - err_count: Default::default(), - unmatched_tts: Default::default(), - bound_count: Default::default(), - } - } -} - -impl Match { +impl Match { fn add_err(&mut self, err: ExpandError) { let prev_err = self.err.take(); self.err = prev_err.or(Some(err)); @@ -119,16 +108,12 @@ impl Match { } /// Matching errors are added to the `Match`. -pub(super) fn match_( - pattern: &MetaTemplate, - input: &tt::Subtree, - is_2021: bool, -) -> Match { - let mut res = match_loop(pattern, input, is_2021); +pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree) -> Match { + let mut res = match_loop(pattern, input); res.bound_count = count(res.bindings.bindings()); return res; - fn count<'a, S: 'a>(bindings: impl Iterator>) -> usize { + fn count<'a>(bindings: impl Iterator) -> usize { bindings .map(|it| match it { Binding::Fragment(_) => 1, @@ -141,10 +126,10 @@ pub(super) fn match_( } #[derive(Debug, Clone)] -enum BindingKind { +enum BindingKind { Empty(SmolStr), Optional(SmolStr), - Fragment(SmolStr, Fragment), + Fragment(SmolStr, Fragment), Missing(SmolStr, MetaVarKind), Nested(usize, usize), } @@ -158,18 +143,13 @@ enum LinkNode { Parent { idx: usize, len: usize }, } -struct BindingsBuilder { - nodes: Vec>>>>, +#[derive(Default)] +struct BindingsBuilder { + nodes: Vec>>>, nested: Vec>>, } -impl Default for BindingsBuilder { - fn default() -> Self { - Self { nodes: Default::default(), nested: Default::default() } - } -} - -impl BindingsBuilder { +impl BindingsBuilder { fn alloc(&mut self) -> BindingsIdx { let idx = self.nodes.len(); self.nodes.push(Vec::new()); @@ -206,7 +186,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: &SmolStr, fragment: Fragment) { + fn push_fragment(&mut self, idx: &mut BindingsIdx, var: &SmolStr, fragment: Fragment) { self.nodes[idx.0] .push(LinkNode::Node(Rc::new(BindingKind::Fragment(var.clone(), fragment)))); } @@ -227,11 +207,11 @@ impl BindingsBuilder { idx.0 = new_idx; } - fn build(self, idx: &BindingsIdx) -> Bindings { + fn build(self, idx: &BindingsIdx) -> Bindings { self.build_inner(&self.nodes[idx.0]) } - fn build_inner(&self, link_nodes: &[LinkNode>>]) -> Bindings { + fn build_inner(&self, link_nodes: &[LinkNode>]) -> Bindings { let mut bindings = Bindings::default(); let mut nodes = Vec::new(); self.collect_nodes(link_nodes, &mut nodes); @@ -281,7 +261,7 @@ impl BindingsBuilder { &'a self, id: usize, len: usize, - nested_refs: &mut Vec<&'a [LinkNode>>]>, + nested_refs: &mut Vec<&'a [LinkNode>]>, ) { self.nested[id].iter().take(len).for_each(|it| match it { LinkNode::Node(id) => nested_refs.push(&self.nodes[*id]), @@ -289,7 +269,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 { @@ -300,7 +280,7 @@ 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<'a>(&'a self, id: usize, len: usize, nodes: &mut Vec<&'a BindingKind>) { 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), @@ -309,8 +289,8 @@ impl BindingsBuilder { fn collect_nodes<'a>( &'a self, - link_nodes: &'a [LinkNode>>], - nodes: &mut Vec<&'a BindingKind>, + link_nodes: &'a [LinkNode>], + nodes: &mut Vec<&'a BindingKind>, ) { link_nodes.iter().for_each(|it| match it { LinkNode::Node(it) => nodes.push(it), @@ -320,22 +300,22 @@ impl BindingsBuilder { } #[derive(Debug, Clone)] -struct MatchState<'t, S> { +struct MatchState<'t> { /// The position of the "dot" in this matcher - dot: OpDelimitedIter<'t, S>, + dot: OpDelimitedIter<'t>, /// Token subtree stack /// When matching against matchers with nested delimited submatchers (e.g., `pat ( pat ( .. ) /// pat ) pat`), we need to keep track of the matchers we are descending into. This stack does /// that where the bottom of the stack is the outermost matcher. - stack: SmallVec<[OpDelimitedIter<'t, S>; 4]>, + stack: SmallVec<[OpDelimitedIter<'t>; 4]>, /// The "parent" matcher position if we are in a repetition. That is, the matcher position just /// before we enter the repetition. - up: Option>>, + up: Option>>, /// The separator if we are in a repetition. - sep: Option>, + sep: Option, /// The KleeneOp of this sequence if we are in a repetition. sep_kind: Option, @@ -347,7 +327,7 @@ struct MatchState<'t, S> { bindings: BindingsIdx, /// Cached result of meta variable parsing - meta_result: Option<(TtIter<'t, S>, ExpandResult>>)>, + meta_result: Option<(TtIter<'t, Span>, ExpandResult>)>, /// Is error occurred in this state, will `poised` to "parent" is_error: bool, @@ -372,18 +352,17 @@ struct MatchState<'t, S> { /// - `bb_items`: the set of items that are waiting for the black-box parser. /// - `error_items`: the set of items in errors, used for error-resilient parsing #[inline] -fn match_loop_inner<'t, S: Span>( - src: TtIter<'t, S>, - stack: &[TtIter<'t, S>], - res: &mut Match, - bindings_builder: &mut BindingsBuilder, - cur_items: &mut SmallVec<[MatchState<'t, S>; 1]>, - bb_items: &mut SmallVec<[MatchState<'t, S>; 1]>, - next_items: &mut Vec>, - eof_items: &mut SmallVec<[MatchState<'t, S>; 1]>, - error_items: &mut SmallVec<[MatchState<'t, S>; 1]>, - is_2021: bool, - delim_span: tt::DelimSpan, +fn match_loop_inner<'t>( + src: TtIter<'t, Span>, + stack: &[TtIter<'t, Span>], + res: &mut Match, + bindings_builder: &mut BindingsBuilder, + cur_items: &mut SmallVec<[MatchState<'t>; 1]>, + bb_items: &mut SmallVec<[MatchState<'t>; 1]>, + next_items: &mut Vec>, + eof_items: &mut SmallVec<[MatchState<'t>; 1]>, + error_items: &mut SmallVec<[MatchState<'t>; 1]>, + delim_span: tt::DelimSpan, ) { macro_rules! try_push { ($items: expr, $it:expr) => { @@ -494,7 +473,7 @@ fn match_loop_inner<'t, S: Span>( OpDelimited::Op(Op::Var { kind, name, .. }) => { if let &Some(kind) = kind { let mut fork = src.clone(); - let match_res = match_meta_var(kind, &mut fork, is_2021, delim_span); + let match_res = match_meta_var(kind, &mut fork, delim_span); match match_res.err { None => { // Some meta variables are optional (e.g. vis) @@ -607,10 +586,10 @@ fn match_loop_inner<'t, S: Span>( } } -fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, is_2021: bool) -> Match { +fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match { let span = src.delimiter.delim_span(); let mut src = TtIter::new(src); - let mut stack: SmallVec<[TtIter<'_, S>; 1]> = SmallVec::new(); + let mut stack: SmallVec<[TtIter<'_, Span>; 1]> = SmallVec::new(); let mut res = Match::default(); let mut error_recover_item = None; @@ -647,7 +626,6 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, is_2021: &mut next_items, &mut eof_items, &mut error_items, - is_2021, span, ); stdx::always!(cur_items.is_empty()); @@ -758,12 +736,11 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, is_2021: } } -fn match_meta_var( +fn match_meta_var( kind: MetaVarKind, - input: &mut TtIter<'_, S>, - is_2021: bool, - delim_span: DelimSpan, -) -> ExpandResult>> { + input: &mut TtIter<'_, Span>, + delim_span: DelimSpan, +) -> ExpandResult> { let fragment = match kind { MetaVarKind::Path => { return input.expect_fragment(parser::PrefixEntryPoint::Path).map(|it| { @@ -771,8 +748,7 @@ fn match_meta_var( }); } MetaVarKind::Ty => parser::PrefixEntryPoint::Ty, - MetaVarKind::Pat if is_2021 => parser::PrefixEntryPoint::PatTop, - MetaVarKind::Pat => parser::PrefixEntryPoint::Pat, + MetaVarKind::Pat => parser::PrefixEntryPoint::PatTop, MetaVarKind::PatParam => parser::PrefixEntryPoint::Pat, MetaVarKind::Stmt => parser::PrefixEntryPoint::Stmt, MetaVarKind::Block => parser::PrefixEntryPoint::Block, @@ -846,7 +822,7 @@ fn match_meta_var( input.expect_fragment(fragment).map(|it| it.map(Fragment::Tokens)) } -fn collect_vars(collector_fun: &mut impl FnMut(SmolStr), pattern: &MetaTemplate) { +fn collect_vars(collector_fun: &mut impl FnMut(SmolStr), pattern: &MetaTemplate) { for op in pattern.iter() { match op { Op::Var { name, .. } => collector_fun(name.clone()), @@ -859,11 +835,11 @@ fn collect_vars(collector_fun: &mut impl FnMut(SmolStr), pattern: &Meta } } } -impl MetaTemplate { - fn iter_delimited_with(&self, delimiter: tt::Delimiter) -> OpDelimitedIter<'_, S> { +impl MetaTemplate { + fn iter_delimited_with(&self, delimiter: tt::Delimiter) -> OpDelimitedIter<'_> { OpDelimitedIter { inner: &self.0, idx: 0, delimited: delimiter } } - fn iter_delimited(&self, span: tt::DelimSpan) -> OpDelimitedIter<'_, S> { + fn iter_delimited(&self, span: tt::DelimSpan) -> OpDelimitedIter<'_> { OpDelimitedIter { inner: &self.0, idx: 0, @@ -873,27 +849,27 @@ impl MetaTemplate { } #[derive(Debug, Clone, Copy)] -enum OpDelimited<'a, S> { - Op(&'a Op), +enum OpDelimited<'a> { + Op(&'a Op), Open, Close, } #[derive(Debug, Clone, Copy)] -struct OpDelimitedIter<'a, S> { - inner: &'a [Op], - delimited: tt::Delimiter, +struct OpDelimitedIter<'a> { + inner: &'a [Op], + delimited: tt::Delimiter, idx: usize, } -impl<'a, S: Span> OpDelimitedIter<'a, S> { +impl<'a> OpDelimitedIter<'a> { fn is_eof(&self) -> bool { let len = self.inner.len() + if self.delimited.kind != tt::DelimiterKind::Invisible { 2 } else { 0 }; self.idx >= len } - fn peek(&self) -> Option> { + fn peek(&self) -> Option> { match self.delimited.kind { tt::DelimiterKind::Invisible => self.inner.get(self.idx).map(OpDelimited::Op), _ => match self.idx { @@ -909,8 +885,8 @@ impl<'a, S: Span> OpDelimitedIter<'a, S> { } } -impl<'a, S: Span> Iterator for OpDelimitedIter<'a, S> { - type Item = OpDelimited<'a, S>; +impl<'a> Iterator for OpDelimitedIter<'a> { + type Item = OpDelimited<'a>; fn next(&mut self) -> Option { let res = self.peek(); @@ -926,8 +902,8 @@ impl<'a, S: Span> Iterator for OpDelimitedIter<'a, S> { } } -impl TtIter<'_, S> { - fn expect_separator(&mut self, separator: &Separator) -> bool { +impl TtIter<'_, Span> { + fn expect_separator(&mut self, separator: &Separator) -> bool { let mut fork = self.clone(); let ok = match separator { Separator::Ident(lhs) => match fork.expect_ident_or_underscore() { @@ -957,7 +933,7 @@ impl TtIter<'_, S> { ok } - fn expect_tt(&mut self) -> Result, ()> { + fn expect_tt(&mut self) -> Result, ()> { if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = self.peek_n(0) { if punct.char == '\'' { self.expect_lifetime() @@ -976,7 +952,7 @@ impl TtIter<'_, S> { } } - fn expect_lifetime(&mut self) -> Result, ()> { + fn expect_lifetime(&mut self) -> Result, ()> { let punct = self.expect_single_punct()?; if punct.char != '\'' { return Err(()); @@ -997,7 +973,7 @@ impl TtIter<'_, S> { .into()) } - fn eat_char(&mut self, c: char) -> Option> { + fn eat_char(&mut self, c: char) -> Option> { let mut fork = self.clone(); match fork.expect_char(c) { Ok(_) => { 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 6d3055da28608..5e6e45f1521b8 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs @@ -1,8 +1,9 @@ //! Transcriber takes a template, like `fn $ident() {}`, a set of bindings like //! `$ident => foo`, interpolates variables in the template, to get `fn foo() {}` +use span::Span; use syntax::SmolStr; -use tt::{Delimiter, Span}; +use tt::Delimiter; use crate::{ expander::{Binding, Bindings, Fragment}, @@ -10,8 +11,8 @@ use crate::{ CountError, ExpandError, ExpandResult, MetaTemplate, }; -impl Bindings { - fn get(&self, name: &str) -> Result<&Binding, ExpandError> { +impl Bindings { + fn get(&self, name: &str) -> Result<&Binding, ExpandError> { match self.inner.get(name) { Some(binding) => Ok(binding), None => Err(ExpandError::UnresolvedBinding(Box::new(Box::from(name)))), @@ -21,10 +22,10 @@ impl Bindings { fn get_fragment( &self, name: &str, - mut span: S, + mut span: Span, nesting: &mut [NestingState], - marker: impl Fn(&mut S), - ) -> Result, ExpandError> { + marker: impl Fn(&mut Span), + ) -> Result { macro_rules! binding_err { ($($arg:tt)*) => { ExpandError::binding_error(format!($($arg)*)) }; } @@ -134,15 +135,15 @@ impl Bindings { } } -pub(super) fn transcribe( - template: &MetaTemplate, - bindings: &Bindings, - marker: impl Fn(&mut S) + Copy, +pub(super) fn transcribe( + template: &MetaTemplate, + bindings: &Bindings, + marker: impl Fn(&mut Span) + Copy, new_meta_vars: bool, - call_site: S, -) -> ExpandResult> { + call_site: Span, +) -> ExpandResult> { let mut ctx = ExpandCtx { bindings, nesting: Vec::new(), new_meta_vars, call_site }; - let mut arena: Vec> = Vec::new(); + let mut arena: Vec> = Vec::new(); expand_subtree(&mut ctx, template, None, &mut arena, marker) } @@ -158,20 +159,20 @@ struct NestingState { } #[derive(Debug)] -struct ExpandCtx<'a, S> { - bindings: &'a Bindings, +struct ExpandCtx<'a> { + bindings: &'a Bindings, nesting: Vec, new_meta_vars: bool, - call_site: S, + call_site: Span, } -fn expand_subtree( - ctx: &mut ExpandCtx<'_, S>, - template: &MetaTemplate, - delimiter: Option>, - arena: &mut Vec>, - marker: impl Fn(&mut S) + Copy, -) -> ExpandResult> { +fn expand_subtree( + ctx: &mut ExpandCtx<'_>, + template: &MetaTemplate, + delimiter: Option>, + arena: &mut Vec>, + 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(); let mut err = None; @@ -332,12 +333,12 @@ fn expand_subtree( } } -fn expand_var( - ctx: &mut ExpandCtx<'_, S>, +fn expand_var( + ctx: &mut ExpandCtx<'_>, v: &SmolStr, - id: S, - marker: impl Fn(&mut S), -) -> ExpandResult> { + id: Span, + marker: impl Fn(&mut Span), +) -> ExpandResult { // We already handle $crate case in mbe parser debug_assert!(v != "crate"); @@ -378,15 +379,15 @@ fn expand_var( } } -fn expand_repeat( - ctx: &mut ExpandCtx<'_, S>, - template: &MetaTemplate, +fn expand_repeat( + ctx: &mut ExpandCtx<'_>, + template: &MetaTemplate, kind: RepeatKind, - separator: &Option>, - arena: &mut Vec>, - marker: impl Fn(&mut S) + Copy, -) -> ExpandResult> { - let mut buf: Vec> = Vec::new(); + separator: &Option, + arena: &mut Vec>, + marker: impl Fn(&mut Span) + Copy, +) -> ExpandResult { + let mut buf: Vec> = Vec::new(); 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 @@ -478,11 +479,7 @@ fn expand_repeat( ExpandResult { value: Fragment::Tokens(tt), err } } -fn push_fragment( - ctx: &ExpandCtx<'_, S>, - buf: &mut Vec>, - fragment: Fragment, -) { +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) => { @@ -494,7 +491,7 @@ fn push_fragment( } } -fn push_subtree(buf: &mut Vec>, tt: tt::Subtree) { +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()), @@ -504,10 +501,10 @@ fn push_subtree(buf: &mut Vec>, tt: tt::Subtree) { /// Inserts the path separator `::` between an identifier and its following generic /// argument list, and then pushes into the buffer. See [`Fragment::Path`] for why /// we need this fixup. -fn fix_up_and_push_path_tt( - ctx: &ExpandCtx<'_, S>, - buf: &mut Vec>, - subtree: tt::Subtree, +fn fix_up_and_push_path_tt( + ctx: &ExpandCtx<'_>, + buf: &mut Vec>, + subtree: tt::Subtree, ) { stdx::always!(matches!(subtree.delimiter.kind, tt::DelimiterKind::Invisible)); let mut prev_was_ident = false; @@ -546,11 +543,7 @@ fn fix_up_and_push_path_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, -) -> Result { +fn count(binding: &Binding, depth_curr: usize, depth_max: usize) -> Result { match binding { Binding::Nested(bs) => { if depth_curr == depth_max { @@ -564,8 +557,8 @@ fn count( } } -fn count_old( - binding: &Binding, +fn count_old( + binding: &Binding, our_depth: usize, count_depth: Option, ) -> Result { diff --git a/src/tools/rust-analyzer/crates/mbe/src/lib.rs b/src/tools/rust-analyzer/crates/mbe/src/lib.rs index 62fdce36892f2..3a853512660eb 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/lib.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/lib.rs @@ -17,8 +17,8 @@ mod tt_iter; #[cfg(test)] mod benchmark; +use span::{Edition, Span, SyntaxContextId}; use stdx::impl_from; -use tt::Span; use std::fmt; @@ -127,32 +127,29 @@ impl fmt::Display for CountError { /// `tt::TokenTree`, but there's a crucial difference: in macro rules, `$ident` /// and `$()*` have special meaning (see `Var` and `Repeat` data structures) #[derive(Clone, Debug, PartialEq, Eq)] -pub struct DeclarativeMacro { - rules: Box<[Rule]>, - // This is used for correctly determining the behavior of the pat fragment - // FIXME: This should be tracked by hygiene of the fragment identifier! - is_2021: bool, +pub struct DeclarativeMacro { + rules: Box<[Rule]>, err: Option>, } #[derive(Clone, Debug, PartialEq, Eq)] -struct Rule { - lhs: MetaTemplate, - rhs: MetaTemplate, +struct Rule { + lhs: MetaTemplate, + rhs: MetaTemplate, } -impl DeclarativeMacro { - pub fn from_err(err: ParseError, is_2021: bool) -> DeclarativeMacro { - DeclarativeMacro { rules: Box::default(), is_2021, err: Some(Box::new(err)) } +impl DeclarativeMacro { + pub fn from_err(err: ParseError) -> DeclarativeMacro { + DeclarativeMacro { rules: Box::default(), err: Some(Box::new(err)) } } /// The old, `macro_rules! m {}` flavor. pub fn parse_macro_rules( - tt: &tt::Subtree, - is_2021: bool, + tt: &tt::Subtree, + edition: impl Copy + Fn(SyntaxContextId) -> Edition, // FIXME: Remove this once we drop support for rust 1.76 (defaults to true then) new_meta_vars: bool, - ) -> DeclarativeMacro { + ) -> DeclarativeMacro { // Note: this parsing can be implemented using mbe machinery itself, by // matching against `$($lhs:tt => $rhs:tt);*` pattern, but implementing // manually seems easier. @@ -161,7 +158,7 @@ impl DeclarativeMacro { let mut err = None; while src.len() > 0 { - let rule = match Rule::parse(&mut src, true, new_meta_vars) { + let rule = match Rule::parse(edition, &mut src, true, new_meta_vars) { Ok(it) => it, Err(e) => { err = Some(Box::new(e)); @@ -184,16 +181,16 @@ impl DeclarativeMacro { } } - DeclarativeMacro { rules: rules.into_boxed_slice(), is_2021, err } + DeclarativeMacro { rules: rules.into_boxed_slice(), err } } /// The new, unstable `macro m {}` flavor. pub fn parse_macro2( - tt: &tt::Subtree, - is_2021: bool, + tt: &tt::Subtree, + edition: impl Copy + Fn(SyntaxContextId) -> Edition, // FIXME: Remove this once we drop support for rust 1.76 (defaults to true then) new_meta_vars: bool, - ) -> DeclarativeMacro { + ) -> DeclarativeMacro { let mut src = TtIter::new(tt); let mut rules = Vec::new(); let mut err = None; @@ -201,7 +198,7 @@ impl DeclarativeMacro { if tt::DelimiterKind::Brace == tt.delimiter.kind { cov_mark::hit!(parse_macro_def_rules); while src.len() > 0 { - let rule = match Rule::parse(&mut src, true, new_meta_vars) { + let rule = match Rule::parse(edition, &mut src, true, new_meta_vars) { Ok(it) => it, Err(e) => { err = Some(Box::new(e)); @@ -220,7 +217,7 @@ impl DeclarativeMacro { } } else { cov_mark::hit!(parse_macro_def_simple); - match Rule::parse(&mut src, false, new_meta_vars) { + match Rule::parse(edition, &mut src, false, new_meta_vars) { Ok(rule) => { if src.len() != 0 { err = Some(Box::new(ParseError::expected("remaining tokens in macro def"))); @@ -240,7 +237,7 @@ impl DeclarativeMacro { } } - DeclarativeMacro { rules: rules.into_boxed_slice(), is_2021, err } + DeclarativeMacro { rules: rules.into_boxed_slice(), err } } pub fn err(&self) -> Option<&ParseError> { @@ -249,18 +246,19 @@ impl DeclarativeMacro { pub fn expand( &self, - tt: &tt::Subtree, - marker: impl Fn(&mut S) + Copy, + tt: &tt::Subtree, + marker: impl Fn(&mut Span) + Copy, new_meta_vars: bool, - call_site: S, - ) -> ExpandResult> { - expander::expand_rules(&self.rules, tt, marker, self.is_2021, new_meta_vars, call_site) + call_site: Span, + ) -> ExpandResult> { + expander::expand_rules(&self.rules, tt, marker, new_meta_vars, call_site) } } -impl Rule { +impl Rule { fn parse( - src: &mut TtIter<'_, S>, + edition: impl Copy + Fn(SyntaxContextId) -> Edition, + src: &mut TtIter<'_, Span>, expect_arrow: bool, new_meta_vars: bool, ) -> Result { @@ -271,14 +269,14 @@ impl Rule { } let rhs = src.expect_subtree().map_err(|()| ParseError::expected("expected subtree"))?; - let lhs = MetaTemplate::parse_pattern(lhs)?; - let rhs = MetaTemplate::parse_template(rhs, new_meta_vars)?; + let lhs = MetaTemplate::parse_pattern(edition, lhs)?; + let rhs = MetaTemplate::parse_template(edition, rhs, new_meta_vars)?; Ok(crate::Rule { lhs, rhs }) } } -fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> { +fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> { for op in pattern.iter() { match op { Op::Subtree { tokens, .. } => validate(tokens)?, diff --git a/src/tools/rust-analyzer/crates/mbe/src/parser.rs b/src/tools/rust-analyzer/crates/mbe/src/parser.rs index afdbbef231476..eaf2fd8c273f8 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/parser.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/parser.rs @@ -2,8 +2,8 @@ //! trees. use smallvec::{smallvec, SmallVec}; +use span::{Edition, Span, SyntaxContextId}; use syntax::SmolStr; -use tt::Span; use crate::{tt_iter::TtIter, ParseError}; @@ -21,30 +21,39 @@ use crate::{tt_iter::TtIter, ParseError}; /// Stuff to the right is a [`MetaTemplate`] template which is used to produce /// output. #[derive(Clone, Debug, PartialEq, Eq)] -pub(crate) struct MetaTemplate(pub(crate) Box<[Op]>); +pub(crate) struct MetaTemplate(pub(crate) Box<[Op]>); -impl MetaTemplate { - pub(crate) fn parse_pattern(pattern: &tt::Subtree) -> Result { - MetaTemplate::parse(pattern, Mode::Pattern, false) +impl MetaTemplate { + pub(crate) fn parse_pattern( + edition: impl Copy + Fn(SyntaxContextId) -> Edition, + pattern: &tt::Subtree, + ) -> Result { + MetaTemplate::parse(edition, pattern, Mode::Pattern, false) } pub(crate) fn parse_template( - template: &tt::Subtree, + edition: impl Copy + Fn(SyntaxContextId) -> Edition, + template: &tt::Subtree, new_meta_vars: bool, ) -> Result { - MetaTemplate::parse(template, Mode::Template, new_meta_vars) + MetaTemplate::parse(edition, template, Mode::Template, new_meta_vars) } - pub(crate) fn iter(&self) -> impl Iterator> { + pub(crate) fn iter(&self) -> impl Iterator { self.0.iter() } - fn parse(tt: &tt::Subtree, mode: Mode, new_meta_vars: bool) -> Result { + fn parse( + edition: impl Copy + Fn(SyntaxContextId) -> Edition, + tt: &tt::Subtree, + mode: Mode, + new_meta_vars: bool, + ) -> Result { let mut src = TtIter::new(tt); let mut res = Vec::new(); while let Some(first) = src.peek_n(0) { - let op = next_op(first, &mut src, mode, new_meta_vars)?; + let op = next_op(edition, first, &mut src, mode, new_meta_vars)?; res.push(op); } @@ -53,15 +62,15 @@ impl MetaTemplate { } #[derive(Clone, Debug, PartialEq, Eq)] -pub(crate) enum Op { +pub(crate) enum Op { Var { name: SmolStr, kind: Option, - id: S, + id: Span, }, Ignore { name: SmolStr, - id: S, + id: Span, }, Index { depth: usize, @@ -75,17 +84,17 @@ pub(crate) enum Op { depth: Option, }, Repeat { - tokens: MetaTemplate, + tokens: MetaTemplate, kind: RepeatKind, - separator: Option>, + separator: Option, }, Subtree { - tokens: MetaTemplate, - delimiter: tt::Delimiter, + tokens: MetaTemplate, + delimiter: tt::Delimiter, }, - Literal(tt::Literal), - Punct(SmallVec<[tt::Punct; 3]>), - Ident(tt::Ident), + Literal(tt::Literal), + Punct(SmallVec<[tt::Punct; 3]>), + Ident(tt::Ident), } #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -114,15 +123,15 @@ pub(crate) enum MetaVarKind { } #[derive(Clone, Debug, Eq)] -pub(crate) enum Separator { - Literal(tt::Literal), - Ident(tt::Ident), - Puncts(SmallVec<[tt::Punct; 3]>), +pub(crate) enum Separator { + Literal(tt::Literal), + Ident(tt::Ident), + Puncts(SmallVec<[tt::Punct; 3]>), } // Note that when we compare a Separator, we just care about its textual value. -impl PartialEq for Separator { - fn eq(&self, other: &Separator) -> bool { +impl PartialEq for Separator { + fn eq(&self, other: &Separator) -> bool { use Separator::*; match (self, other) { @@ -144,12 +153,13 @@ enum Mode { Template, } -fn next_op( - first_peeked: &tt::TokenTree, - src: &mut TtIter<'_, S>, +fn next_op( + edition: impl Copy + Fn(SyntaxContextId) -> Edition, + first_peeked: &tt::TokenTree, + src: &mut TtIter<'_, Span>, mode: Mode, new_meta_vars: bool, -) -> Result, ParseError> { +) -> Result { let res = match first_peeked { tt::TokenTree::Leaf(tt::Leaf::Punct(p @ tt::Punct { char: '$', .. })) => { src.next().expect("first token already peeked"); @@ -162,7 +172,7 @@ fn next_op( tt::TokenTree::Subtree(subtree) => match subtree.delimiter.kind { tt::DelimiterKind::Parenthesis => { let (separator, kind) = parse_repeat(src)?; - let tokens = MetaTemplate::parse(subtree, mode, new_meta_vars)?; + let tokens = MetaTemplate::parse(edition, subtree, mode, new_meta_vars)?; Op::Repeat { tokens, separator, kind } } tt::DelimiterKind::Brace => match mode { @@ -189,13 +199,13 @@ fn next_op( Op::Ident(tt::Ident { text: "$crate".into(), span: ident.span }) } tt::Leaf::Ident(ident) => { - let kind = eat_fragment_kind(src, mode)?; + let kind = eat_fragment_kind(edition, src, mode)?; let name = ident.text.clone(); let id = ident.span; Op::Var { name, kind, id } } tt::Leaf::Literal(lit) if is_boolean_literal(lit) => { - let kind = eat_fragment_kind(src, mode)?; + let kind = eat_fragment_kind(edition, src, mode)?; let name = lit.text.clone(); let id = lit.span; Op::Var { name, kind, id } @@ -233,15 +243,16 @@ fn next_op( tt::TokenTree::Subtree(subtree) => { src.next().expect("first token already peeked"); - let tokens = MetaTemplate::parse(subtree, mode, new_meta_vars)?; + let tokens = MetaTemplate::parse(edition, subtree, mode, new_meta_vars)?; Op::Subtree { tokens, delimiter: subtree.delimiter } } }; Ok(res) } -fn eat_fragment_kind( - src: &mut TtIter<'_, S>, +fn eat_fragment_kind( + edition: impl Copy + Fn(SyntaxContextId) -> Edition, + src: &mut TtIter<'_, Span>, mode: Mode, ) -> Result, ParseError> { if let Mode::Pattern = mode { @@ -252,7 +263,10 @@ fn eat_fragment_kind( let kind = match ident.text.as_str() { "path" => MetaVarKind::Path, "ty" => MetaVarKind::Ty, - "pat" => MetaVarKind::Pat, + "pat" => match edition(ident.span.ctx) { + Edition::Edition2015 | Edition::Edition2018 => MetaVarKind::PatParam, + Edition::Edition2021 | Edition::Edition2024 => MetaVarKind::Pat, + }, "pat_param" => MetaVarKind::PatParam, "stmt" => MetaVarKind::Stmt, "block" => MetaVarKind::Block, @@ -271,13 +285,11 @@ fn eat_fragment_kind( Ok(None) } -fn is_boolean_literal(lit: &tt::Literal) -> bool { +fn is_boolean_literal(lit: &tt::Literal) -> bool { matches!(lit.text.as_str(), "true" | "false") } -fn parse_repeat( - src: &mut TtIter<'_, S>, -) -> Result<(Option>, RepeatKind), ParseError> { +fn parse_repeat(src: &mut TtIter<'_, Span>) -> Result<(Option, RepeatKind), ParseError> { let mut separator = Separator::Puncts(SmallVec::new()); for tt in src { let tt = match tt { @@ -314,7 +326,7 @@ fn parse_repeat( Err(ParseError::InvalidRepeat) } -fn parse_metavar_expr(new_meta_vars: bool, src: &mut TtIter<'_, S>) -> Result, ()> { +fn parse_metavar_expr(new_meta_vars: bool, src: &mut TtIter<'_, Span>) -> Result { let func = src.expect_ident()?; let args = src.expect_subtree()?; @@ -352,7 +364,7 @@ fn parse_metavar_expr(new_meta_vars: bool, src: &mut TtIter<'_, S>) -> Ok(op) } -fn parse_depth(src: &mut TtIter<'_, S>) -> Result { +fn parse_depth(src: &mut TtIter<'_, Span>) -> Result { if src.len() == 0 { Ok(0) } else if let tt::Leaf::Literal(lit) = src.expect_literal()? { @@ -363,7 +375,7 @@ fn parse_depth(src: &mut TtIter<'_, S>) -> Result { } } -fn try_eat_comma(src: &mut TtIter<'_, S>) -> bool { +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) { let _ = src.next(); return true; diff --git a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs index 57d6082dd7051..c934db6b715a8 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs @@ -1,5 +1,7 @@ //! Conversions between [`SyntaxNode`] and [`tt::TokenTree`]. +use std::fmt; + use rustc_hash::{FxHashMap, FxHashSet}; use span::{SpanAnchor, SpanData, SpanMap}; use stdx::{never, non_empty_vec::NonEmptyVec}; @@ -9,30 +11,27 @@ use syntax::{ SyntaxKind::*, SyntaxNode, SyntaxToken, SyntaxTreeBuilder, TextRange, TextSize, WalkEvent, T, }; -use tt::{ - buffer::{Cursor, TokenBuffer}, - Span, -}; +use tt::buffer::{Cursor, TokenBuffer}; use crate::{to_parser_input::to_parser_input, tt_iter::TtIter}; #[cfg(test)] mod tests; -pub trait SpanMapper { +pub trait SpanMapper { fn span_for(&self, range: TextRange) -> S; } impl SpanMapper> for SpanMap where - SpanData: Span, + SpanData: Copy, { fn span_for(&self, range: TextRange) -> SpanData { self.span_at(range.start()) } } -impl> SpanMapper for &SM { +impl> SpanMapper for &SM { fn span_for(&self, range: TextRange) -> S { SM::span_for(self, range) } @@ -78,8 +77,7 @@ pub fn syntax_node_to_token_tree( span: SpanData, ) -> tt::Subtree> where - SpanData: Span, - Ctx: Copy, + SpanData: Copy + fmt::Debug, SpanMap: SpanMapper>, { let mut c = Converter::new(node, map, Default::default(), Default::default(), span); @@ -98,8 +96,7 @@ pub fn syntax_node_to_token_tree_modified( ) -> tt::Subtree> where SpanMap: SpanMapper>, - SpanData: Span, - Ctx: Copy, + SpanData: Copy + fmt::Debug, { let mut c = Converter::new(node, map, append, remove, call_site); convert_tokens(&mut c) @@ -124,8 +121,7 @@ pub fn token_tree_to_syntax_node( entry_point: parser::TopEntryPoint, ) -> (Parse, SpanMap) where - SpanData: Span, - Ctx: Copy, + SpanData: Copy + fmt::Debug, { let buffer = match tt { tt::Subtree { @@ -161,7 +157,7 @@ pub fn parse_to_token_tree( text: &str, ) -> Option>> where - SpanData: Span, + SpanData: Copy + fmt::Debug, Ctx: Copy, { let lexed = parser::LexedStr::new(text); @@ -175,7 +171,7 @@ where /// Convert a string to a `TokenTree`. The passed span will be used for all spans of the produced subtree. pub fn parse_to_token_tree_static_span(span: S, text: &str) -> Option> where - S: Span, + S: Copy + fmt::Debug, { let lexed = parser::LexedStr::new(text); if lexed.errors().next().is_some() { @@ -186,11 +182,10 @@ where } /// Split token tree with separate expr: $($e:expr)SEP* -pub fn parse_exprs_with_sep( - tt: &tt::Subtree, - sep: char, - span: S, -) -> Vec> { +pub fn parse_exprs_with_sep(tt: &tt::Subtree, sep: char, span: S) -> Vec> +where + S: Copy + fmt::Debug, +{ if tt.token_trees.is_empty() { return Vec::new(); } @@ -226,7 +221,8 @@ pub fn parse_exprs_with_sep( fn convert_tokens(conv: &mut C) -> tt::Subtree where C: TokenConverter, - S: Span, + S: Copy + fmt::Debug, + C::Token: fmt::Debug, { let entry = tt::SubtreeBuilder { delimiter: tt::Delimiter::invisible_spanned(conv.call_site()), @@ -485,7 +481,7 @@ struct StaticRawConverter<'a, S> { span: S, } -trait SrcToken: std::fmt::Debug { +trait SrcToken { fn kind(&self, ctx: &Ctx) -> SyntaxKind; fn to_char(&self, ctx: &Ctx) -> Option; @@ -525,7 +521,7 @@ impl SrcToken, S> for usize { } } -impl SrcToken, S> for usize { +impl SrcToken, S> for usize { fn kind(&self, ctx: &StaticRawConverter<'_, S>) -> SyntaxKind { ctx.lexed.kind(*self) } @@ -541,7 +537,7 @@ impl SrcToken, S> for usize { impl TokenConverter> for RawConverter<'_, Ctx> where - SpanData: Span, + SpanData: Copy, { type Token = usize; @@ -584,7 +580,7 @@ where impl TokenConverter for StaticRawConverter<'_, S> where - S: Span, + S: Copy, { type Token = usize; @@ -709,7 +705,7 @@ impl SynToken { } } -impl SrcToken, S> for SynToken { +impl SrcToken, S> for SynToken { fn kind(&self, _ctx: &Converter) -> SyntaxKind { match self { SynToken::Ordinary(token) => token.kind(), @@ -748,7 +744,7 @@ impl SrcToken, S> for SynToke impl TokenConverter for Converter where - S: Span, + S: Copy, SpanMap: SpanMapper, { type Token = SynToken; @@ -828,7 +824,7 @@ where struct TtTreeSink<'a, Ctx> where - SpanData: Span, + SpanData: Copy, { buf: String, cursor: Cursor<'a, SpanData>, @@ -839,7 +835,7 @@ where impl<'a, Ctx> TtTreeSink<'a, Ctx> where - SpanData: Span, + SpanData: Copy, { fn new(cursor: Cursor<'a, SpanData>) -> Self { TtTreeSink { @@ -871,7 +867,7 @@ fn delim_to_str(d: tt::DelimiterKind, closing: bool) -> Option<&'static str> { impl TtTreeSink<'_, Ctx> where - SpanData: Span, + SpanData: Copy, { /// Parses a float literal as if it was a one to two name ref nodes with a dot inbetween. /// This occurs when a float literal is used as a field access. diff --git a/src/tools/rust-analyzer/crates/mbe/src/to_parser_input.rs b/src/tools/rust-analyzer/crates/mbe/src/to_parser_input.rs index 00a14f04686e2..3f70149aa5eae 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/to_parser_input.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/to_parser_input.rs @@ -1,11 +1,13 @@ //! Convert macro-by-example tokens which are specific to macro expansion into a //! format that works for our parser. +use std::fmt; + use syntax::{SyntaxKind, SyntaxKind::*, T}; -use tt::{buffer::TokenBuffer, Span}; +use tt::buffer::TokenBuffer; -pub(crate) fn to_parser_input(buffer: &TokenBuffer<'_, S>) -> parser::Input { +pub(crate) fn to_parser_input(buffer: &TokenBuffer<'_, S>) -> parser::Input { let mut res = parser::Input::default(); let mut current = buffer.begin(); diff --git a/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs b/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs index f9913cb6f9b8e..e3d12d87078b3 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs @@ -1,9 +1,10 @@ //! A "Parser" structure for token trees. We use this when parsing a declarative //! macro definition into a list of patterns and templates. +use core::fmt; + use smallvec::{smallvec, SmallVec}; use syntax::SyntaxKind; -use tt::Span; use crate::{to_parser_input::to_parser_input, ExpandError, ExpandResult}; @@ -12,7 +13,7 @@ pub(crate) struct TtIter<'a, S> { pub(crate) inner: std::slice::Iter<'a, tt::TokenTree>, } -impl<'a, S: Span> TtIter<'a, S> { +impl<'a, S: Copy> TtIter<'a, S> { pub(crate) fn new(subtree: &'a tt::Subtree) -> TtIter<'a, S> { TtIter { inner: subtree.token_trees.iter() } } @@ -130,7 +131,12 @@ impl<'a, S: Span> TtIter<'a, S> { _ => Ok(smallvec![first]), } } + pub(crate) fn peek_n(&self, n: usize) -> Option<&'a tt::TokenTree> { + self.inner.as_slice().get(n) + } +} +impl<'a, S: Copy + fmt::Debug> TtIter<'a, S> { pub(crate) fn expect_fragment( &mut self, entry_point: parser::PrefixEntryPoint, @@ -185,10 +191,6 @@ impl<'a, S: Span> TtIter<'a, S> { }; ExpandResult { value: res, err } } - - pub(crate) fn peek_n(&self, n: usize) -> Option<&'a tt::TokenTree> { - self.inner.as_slice().get(n) - } } impl<'a, S> Iterator for TtIter<'a, S> { diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs index 72848a1f2b79f..54ed5f0ba2368 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs @@ -490,6 +490,18 @@ fn match_expr(p: &mut Parser<'_>) -> CompletedMarker { m.complete(p, MATCH_EXPR) } +// test_err match_arms_recovery +// fn foo() { +// match () { +// _ => (),, +// _ => , +// _ => (), +// => (), +// if true => (), +// _ => (), +// () if => (), +// } +// } pub(crate) fn match_arm_list(p: &mut Parser<'_>) { assert!(p.at(T!['{'])); let m = p.start(); @@ -511,6 +523,10 @@ pub(crate) fn match_arm_list(p: &mut Parser<'_>) { error_block(p, "expected match arm"); continue; } + if p.at(T![,]) { + p.err_and_bump("expected pattern"); + continue; + } match_arm(p); } p.expect(T!['}']); @@ -544,26 +560,30 @@ fn match_arm(p: &mut Parser<'_>) { // } attributes::outer_attrs(p); - patterns::pattern_top_r(p, TokenSet::EMPTY); + patterns::pattern_top_r(p, TokenSet::new(&[T![=], T![if]])); if p.at(T![if]) { match_guard(p); } p.expect(T![=>]); - let blocklike = match expr_stmt(p, None) { - Some((_, blocklike)) => blocklike, - None => BlockLike::NotBlock, - }; - - // test match_arms_commas - // fn foo() { - // match () { - // _ => (), - // _ => {} - // _ => () - // } - // } - if !p.eat(T![,]) && !blocklike.is_block() && !p.at(T!['}']) { - p.error("expected `,`"); + if p.eat(T![,]) { + p.error("expected expression"); + } else { + let blocklike = match expr_stmt(p, None) { + Some((_, blocklike)) => blocklike, + None => BlockLike::NotBlock, + }; + + // test match_arms_commas + // fn foo() { + // match () { + // _ => (), + // _ => {} + // _ => () + // } + // } + if !p.eat(T![,]) && !blocklike.is_block() && !p.at(T!['}']) { + p.error("expected `,`"); + } } m.complete(p, MATCH_ARM); } @@ -579,7 +599,11 @@ fn match_guard(p: &mut Parser<'_>) -> CompletedMarker { assert!(p.at(T![if])); let m = p.start(); p.bump(T![if]); - expr(p); + if p.at(T![=]) { + p.error("expected expression"); + } else { + expr(p); + } m.complete(p, MATCH_GUARD) } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rast new file mode 100644 index 0000000000000..5b191945e4573 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rast @@ -0,0 +1,113 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + MATCH_EXPR + MATCH_KW "match" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + MATCH_ARM_LIST + L_CURLY "{" + WHITESPACE "\n " + MATCH_ARM + WILDCARD_PAT + UNDERSCORE "_" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + COMMA "," + ERROR + COMMA "," + WHITESPACE "\n " + MATCH_ARM + WILDCARD_PAT + UNDERSCORE "_" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + COMMA "," + WHITESPACE "\n " + MATCH_ARM + WILDCARD_PAT + UNDERSCORE "_" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + COMMA "," + WHITESPACE "\n " + MATCH_ARM + FAT_ARROW "=>" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + COMMA "," + WHITESPACE "\n " + MATCH_ARM + MATCH_GUARD + IF_KW "if" + WHITESPACE " " + LITERAL + TRUE_KW "true" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + COMMA "," + WHITESPACE "\n " + MATCH_ARM + WILDCARD_PAT + UNDERSCORE "_" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + COMMA "," + WHITESPACE "\n " + MATCH_ARM + TUPLE_PAT + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + MATCH_GUARD + IF_KW "if" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + COMMA "," + WHITESPACE "\n " + R_CURLY "}" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" +error 42: expected pattern +error 58: expected expression +error 85: expected pattern +error 100: expected pattern +error 145: expected expression diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rs new file mode 100644 index 0000000000000..173103b2e3797 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rs @@ -0,0 +1,11 @@ +fn foo() { + match () { + _ => (),, + _ => , + _ => (), + => (), + if true => (), + _ => (), + () if => (), + } +} diff --git a/src/tools/rust-analyzer/crates/paths/Cargo.toml b/src/tools/rust-analyzer/crates/paths/Cargo.toml index 3d8752b5a829d..59a4ad9a25587 100644 --- a/src/tools/rust-analyzer/crates/paths/Cargo.toml +++ b/src/tools/rust-analyzer/crates/paths/Cargo.toml @@ -12,10 +12,14 @@ rust-version.workspace = true doctest = false [dependencies] +camino.workspace = true # Adding this dep sadly puts a lot of rust-analyzer crates after the # serde-derive crate. Even though we don't activate the derive feature here, # someone else in the crate graph certainly does! # serde.workspace = true +[features] +serde1 = ["camino/serde1"] + [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/src/tools/rust-analyzer/crates/paths/src/lib.rs b/src/tools/rust-analyzer/crates/paths/src/lib.rs index a63d251c20d45..2d3653401d2f5 100644 --- a/src/tools/rust-analyzer/crates/paths/src/lib.rs +++ b/src/tools/rust-analyzer/crates/paths/src/lib.rs @@ -1,4 +1,4 @@ -//! Thin wrappers around `std::path`, distinguishing between absolute and +//! Thin wrappers around `std::path`/`camino::path`, distinguishing between absolute and //! relative paths. #![warn(rust_2018_idioms, unused_lifetimes)] @@ -7,16 +7,24 @@ use std::{ borrow::Borrow, ffi::OsStr, fmt, ops, - path::{Component, Path, PathBuf}, + path::{Path, PathBuf}, }; -/// Wrapper around an absolute [`PathBuf`]. -#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] -pub struct AbsPathBuf(PathBuf); +pub use camino::*; + +/// Wrapper around an absolute [`Utf8PathBuf`]. +#[derive(Debug, Clone, Ord, PartialOrd, Eq, Hash)] +pub struct AbsPathBuf(Utf8PathBuf); + +impl From for Utf8PathBuf { + fn from(AbsPathBuf(path_buf): AbsPathBuf) -> Utf8PathBuf { + path_buf + } +} impl From for PathBuf { fn from(AbsPathBuf(path_buf): AbsPathBuf) -> PathBuf { - path_buf + path_buf.into() } } @@ -27,9 +35,21 @@ impl ops::Deref for AbsPathBuf { } } +impl AsRef for AbsPathBuf { + fn as_ref(&self) -> &Utf8Path { + self.0.as_path() + } +} + +impl AsRef for AbsPathBuf { + fn as_ref(&self) -> &OsStr { + self.0.as_ref() + } +} + impl AsRef for AbsPathBuf { fn as_ref(&self) -> &Path { - self.0.as_path() + self.0.as_ref() } } @@ -45,26 +65,36 @@ impl Borrow for AbsPathBuf { } } +impl TryFrom for AbsPathBuf { + type Error = Utf8PathBuf; + fn try_from(path_buf: Utf8PathBuf) -> Result { + if !path_buf.is_absolute() { + return Err(path_buf); + } + Ok(AbsPathBuf(path_buf)) + } +} + impl TryFrom for AbsPathBuf { type Error = PathBuf; fn try_from(path_buf: PathBuf) -> Result { if !path_buf.is_absolute() { return Err(path_buf); } - Ok(AbsPathBuf(path_buf)) + Ok(AbsPathBuf(Utf8PathBuf::from_path_buf(path_buf)?)) } } impl TryFrom<&str> for AbsPathBuf { - type Error = PathBuf; - fn try_from(path: &str) -> Result { - AbsPathBuf::try_from(PathBuf::from(path)) + type Error = Utf8PathBuf; + fn try_from(path: &str) -> Result { + AbsPathBuf::try_from(Utf8PathBuf::from(path)) } } -impl PartialEq for AbsPathBuf { - fn eq(&self, other: &AbsPath) -> bool { - self.as_path() == other +impl + ?Sized> PartialEq

for AbsPathBuf { + fn eq(&self, other: &P) -> bool { + self.0.as_std_path() == other.as_ref() } } @@ -74,19 +104,31 @@ impl AbsPathBuf { /// # Panics /// /// Panics if `path` is not absolute. - pub fn assert(path: PathBuf) -> AbsPathBuf { + pub fn assert(path: Utf8PathBuf) -> AbsPathBuf { AbsPathBuf::try_from(path) - .unwrap_or_else(|path| panic!("expected absolute path, got {}", path.display())) + .unwrap_or_else(|path| panic!("expected absolute path, got {}", path)) + } + + /// Wrap the given absolute path in `AbsPathBuf` + /// + /// # Panics + /// + /// Panics if `path` is not absolute. + pub fn assert_utf8(path: PathBuf) -> AbsPathBuf { + AbsPathBuf::assert( + Utf8PathBuf::from_path_buf(path) + .unwrap_or_else(|path| panic!("expected utf8 path, got {}", path.display())), + ) } /// Coerces to an `AbsPath` slice. /// - /// Equivalent of [`PathBuf::as_path`] for `AbsPathBuf`. + /// Equivalent of [`Utf8PathBuf::as_path`] for `AbsPathBuf`. pub fn as_path(&self) -> &AbsPath { AbsPath::assert(self.0.as_path()) } - /// Equivalent of [`PathBuf::pop`] for `AbsPathBuf`. + /// Equivalent of [`Utf8PathBuf::pop`] for `AbsPathBuf`. /// /// Note that this won't remove the root component, so `self` will still be /// absolute. @@ -97,18 +139,36 @@ impl AbsPathBuf { impl fmt::Display for AbsPathBuf { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.0.display(), f) + fmt::Display::fmt(&self.0, f) } } -/// Wrapper around an absolute [`Path`]. -#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] +/// Wrapper around an absolute [`Utf8Path`]. +#[derive(Debug, Ord, PartialOrd, Eq, Hash)] #[repr(transparent)] -pub struct AbsPath(Path); +pub struct AbsPath(Utf8Path); + +impl + ?Sized> PartialEq

` (where P is one of the previous types except `Self`) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0307`. diff --git a/tests/ui/simd/intrinsic/ptr-cast.rs b/tests/ui/simd/intrinsic/ptr-cast.rs index 83d86baf334a7..0490734b48ac5 100644 --- a/tests/ui/simd/intrinsic/ptr-cast.rs +++ b/tests/ui/simd/intrinsic/ptr-cast.rs @@ -4,8 +4,8 @@ extern "rust-intrinsic" { fn simd_cast_ptr(x: T) -> U; - fn simd_expose_addr(x: T) -> U; - fn simd_from_exposed_addr(x: T) -> U; + fn simd_expose_provenance(x: T) -> U; + fn simd_with_exposed_provenance(x: T) -> U; } #[derive(Copy, Clone)] @@ -22,12 +22,12 @@ fn main() { // change constness and type let const_ptrs: V<*const u8> = simd_cast_ptr(ptrs); - let exposed_addr: V = simd_expose_addr(const_ptrs); + let exposed_addr: V = simd_expose_provenance(const_ptrs); - let from_exposed_addr: V<*mut i8> = simd_from_exposed_addr(exposed_addr); + let with_exposed_provenance: V<*mut i8> = simd_with_exposed_provenance(exposed_addr); assert!(const_ptrs.0 == [ptr as *const u8, core::ptr::null()]); assert!(exposed_addr.0 == [ptr as usize, 0]); - assert!(from_exposed_addr.0 == ptrs.0); + assert!(with_exposed_provenance.0 == ptrs.0); } } diff --git a/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs b/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs new file mode 100644 index 0000000000000..5c96c653df523 --- /dev/null +++ b/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs @@ -0,0 +1,7 @@ +fn main() { + let str::<{fn str() { let str::T>>::as_bytes; }}, T>::as_bytes; +//~^ ERROR expected a pattern, found an expression +//~| ERROR cannot find type `T` in this scope +//~| ERROR const and type arguments are not allowed on builtin type `str` +//~| ERROR expected unit struct, unit variant or constant, found associated function `str<, T>::as_bytes` +} diff --git a/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr b/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr new file mode 100644 index 0000000000000..d62c019a1e192 --- /dev/null +++ b/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr @@ -0,0 +1,36 @@ +error: expected a pattern, found an expression + --> $DIR/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs:2:31 + | +LL | let str::<{fn str() { let str::T>>::as_bytes; }}, T>::as_bytes; + | ^^^^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + +error[E0412]: cannot find type `T` in this scope + --> $DIR/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs:2:55 + | +LL | let str::<{fn str() { let str::T>>::as_bytes; }}, T>::as_bytes; + | ^ not found in this scope + +error[E0109]: const and type arguments are not allowed on builtin type `str` + --> $DIR/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs:2:15 + | +LL | let str::<{fn str() { let str::T>>::as_bytes; }}, T>::as_bytes; + | --- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^ const and type arguments not allowed + | | + | not allowed on builtin type `str` + | +help: primitive type `str` doesn't have generic parameters + | +LL - let str::<{fn str() { let str::T>>::as_bytes; }}, T>::as_bytes; +LL + let str::as_bytes; + | + +error[E0533]: expected unit struct, unit variant or constant, found associated function `str<, T>::as_bytes` + --> $DIR/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs:2:9 + | +LL | let str::<{fn str() { let str::T>>::as_bytes; }}, T>::as_bytes; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a unit struct, unit variant or constant + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0109, E0412, E0533. +For more information about an error, try `rustc --explain E0109`. diff --git a/tests/ui/specialization/issue-39448.rs b/tests/ui/specialization/issue-39448.rs index a15c4bd6b7ffc..1c8843d983a47 100644 --- a/tests/ui/specialization/issue-39448.rs +++ b/tests/ui/specialization/issue-39448.rs @@ -22,6 +22,7 @@ trait FromA { } impl> FromA for U { + //~^ ERROR cycle detected when computing whether impls specialize one another default fn from(x: T) -> Self { ToA::to(x) } @@ -42,7 +43,7 @@ where #[allow(dead_code)] fn foo(x: T, y: U) -> U { - x.foo(y.to()).to() //~ ERROR overflow evaluating the requirement + x.foo(y.to()).to() } fn main() { diff --git a/tests/ui/specialization/issue-39448.stderr b/tests/ui/specialization/issue-39448.stderr index dc5db4f4285cd..e2c5f8c484689 100644 --- a/tests/ui/specialization/issue-39448.stderr +++ b/tests/ui/specialization/issue-39448.stderr @@ -8,28 +8,21 @@ LL | #![feature(specialization)] = help: consider using `min_specialization` instead, which is more stable and complete = note: `#[warn(incomplete_features)]` on by default -error[E0275]: overflow evaluating the requirement `T: FromA` - --> $DIR/issue-39448.rs:45:13 - | -LL | x.foo(y.to()).to() - | ^^ - | -note: required for `T` to implement `FromA` - --> $DIR/issue-39448.rs:24:29 +error[E0391]: cycle detected when computing whether impls specialize one another + --> $DIR/issue-39448.rs:24:1 | LL | impl> FromA for U { - | -------- ^^^^^^^^ ^ - | | - | unsatisfied trait bound introduced here -note: required for `U` to implement `ToA` - --> $DIR/issue-39448.rs:34:12 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: ...which requires evaluating trait selection obligation `u16: FromA`... + = note: ...which again requires computing whether impls specialize one another, completing the cycle +note: cycle used when building specialization graph of trait `FromA` + --> $DIR/issue-39448.rs:20:1 | -LL | impl ToA for T - | ^^^^^^ ^ -LL | where -LL | U: FromA, - | -------- unsatisfied trait bound introduced here +LL | trait FromA { + | ^^^^^^^^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error: aborting due to 1 previous error; 1 warning emitted -For more information about this error, try `rustc --explain E0275`. +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/specialization/issue-39618.rs b/tests/ui/specialization/issue-39618.rs index 5b9b012e66531..14a6fcf572eae 100644 --- a/tests/ui/specialization/issue-39618.rs +++ b/tests/ui/specialization/issue-39618.rs @@ -2,8 +2,6 @@ // FIXME(JohnTitor): Centril pointed out this looks suspicions, we should revisit here. // More context: https://github.com/rust-lang/rust/pull/69192#discussion_r379846796 -//@ check-pass - #![feature(specialization)] //~ WARN the feature `specialization` is incomplete trait Foo { @@ -19,6 +17,7 @@ impl Bar for T where T: Foo { } impl Foo for T where T: Bar { + //~^ ERROR cycle detected when computing whether impls specialize one another fn foo(&self) {} } diff --git a/tests/ui/specialization/issue-39618.stderr b/tests/ui/specialization/issue-39618.stderr index 19de60c7c1787..756162ce92c37 100644 --- a/tests/ui/specialization/issue-39618.stderr +++ b/tests/ui/specialization/issue-39618.stderr @@ -1,5 +1,5 @@ warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-39618.rs:7:12 + --> $DIR/issue-39618.rs:5:12 | LL | #![feature(specialization)] | ^^^^^^^^^^^^^^ @@ -8,5 +8,21 @@ LL | #![feature(specialization)] = help: consider using `min_specialization` instead, which is more stable and complete = note: `#[warn(incomplete_features)]` on by default -warning: 1 warning emitted +error[E0391]: cycle detected when computing whether impls specialize one another + --> $DIR/issue-39618.rs:19:1 + | +LL | impl Foo for T where T: Bar { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: ...which requires evaluating trait selection obligation `u64: Bar`... + = note: ...which again requires computing whether impls specialize one another, completing the cycle +note: cycle used when building specialization graph of trait `Foo` + --> $DIR/issue-39618.rs:7:1 + | +LL | trait Foo { + | ^^^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error: aborting due to 1 previous error; 1 warning emitted +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/specialization/min_specialization/impl-on-opaque.rs b/tests/ui/specialization/min_specialization/impl-on-opaque.rs new file mode 100644 index 0000000000000..7531dcaccf235 --- /dev/null +++ b/tests/ui/specialization/min_specialization/impl-on-opaque.rs @@ -0,0 +1,31 @@ +// Test that specializing on opaque types is allowed + +//@ check-pass + +#![feature(min_specialization, type_alias_impl_trait)] + +trait SpecTrait { + fn f(); +} + +impl SpecTrait for () { + default fn f() {} +} + +type Opaque = impl Tuple; + +trait Tuple {} + +impl Tuple for () {} + +impl SpecTrait for () { + fn f() {} +} + +impl SpecTrait for () { + fn f() {} +} + +fn foo() -> Opaque {} + +fn main() {} diff --git a/tests/ui/specialization/min_specialization/impl-on-opaque2.rs b/tests/ui/specialization/min_specialization/impl-on-opaque2.rs new file mode 100644 index 0000000000000..0cd8be84ed370 --- /dev/null +++ b/tests/ui/specialization/min_specialization/impl-on-opaque2.rs @@ -0,0 +1,28 @@ +// Test that specializing on opaque types is allowed + +#![feature(min_specialization, type_alias_impl_trait)] + +trait SpecTrait { + fn f(); +} + +impl SpecTrait for () { + default fn f() {} +} + +type Opaque = impl Tuple; + +trait Tuple {} + +impl Tuple for () {} + +// FIXME: this passes if we use `<(), ()>` here instead of `<(), Opaque>`, +// even though there can't be more overlap from the opaque version +impl SpecTrait<(), Opaque> for () { + //~^ ERROR: conflicting implementations + fn f() {} +} + +fn foo() -> Opaque {} + +fn main() {} diff --git a/tests/ui/specialization/min_specialization/impl-on-opaque2.stderr b/tests/ui/specialization/min_specialization/impl-on-opaque2.stderr new file mode 100644 index 0000000000000..3c0bc8f8f8354 --- /dev/null +++ b/tests/ui/specialization/min_specialization/impl-on-opaque2.stderr @@ -0,0 +1,12 @@ +error[E0119]: conflicting implementations of trait `SpecTrait<(), ()>` for type `()` + --> $DIR/impl-on-opaque2.rs:21:1 + | +LL | impl SpecTrait for () { + | ------------------------------- first implementation here +... +LL | impl SpecTrait<(), Opaque> for () { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `()` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/stability-attribute/stability-in-private-module.rs b/tests/ui/stability-attribute/stability-in-private-module.rs index f12e9198b0d7c..df94931690b7c 100644 --- a/tests/ui/stability-attribute/stability-in-private-module.rs +++ b/tests/ui/stability-attribute/stability-in-private-module.rs @@ -1,4 +1,4 @@ fn main() { - let _ = std::thread::thread_info::current_thread(); - //~^ERROR module `thread_info` is private + let _ = std::sys::os::errno(); + //~^ERROR module `sys` is private } diff --git a/tests/ui/stability-attribute/stability-in-private-module.stderr b/tests/ui/stability-attribute/stability-in-private-module.stderr index 9eb4d3efc8bd2..e65f8aa9b1fc1 100644 --- a/tests/ui/stability-attribute/stability-in-private-module.stderr +++ b/tests/ui/stability-attribute/stability-in-private-module.stderr @@ -1,13 +1,13 @@ -error[E0603]: module `thread_info` is private - --> $DIR/stability-in-private-module.rs:2:26 +error[E0603]: module `sys` is private + --> $DIR/stability-in-private-module.rs:2:18 | -LL | let _ = std::thread::thread_info::current_thread(); - | ^^^^^^^^^^^ -------------- function `current_thread` is not publicly re-exported - | | - | private module +LL | let _ = std::sys::os::errno(); + | ^^^ ----- function `errno` is not publicly re-exported + | | + | private module | -note: the module `thread_info` is defined here - --> $SRC_DIR/std/src/thread/mod.rs:LL:COL +note: the module `sys` is defined here + --> $SRC_DIR/std/src/lib.rs:LL:COL error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-1660.rs b/tests/ui/static/issue-1660.rs similarity index 100% rename from tests/ui/issues/issue-1660.rs rename to tests/ui/static/issue-1660.rs diff --git a/tests/ui/statics/nested-allocations-dont-inherit-codegen-attrs.rs b/tests/ui/statics/nested-allocations-dont-inherit-codegen-attrs.rs new file mode 100644 index 0000000000000..0b7e659c7b75a --- /dev/null +++ b/tests/ui/statics/nested-allocations-dont-inherit-codegen-attrs.rs @@ -0,0 +1,11 @@ +//@ build-pass + +// Make sure that the nested static allocation for `FOO` doesn't inherit `no_mangle`. +#[no_mangle] +pub static mut FOO: &mut [i32] = &mut [42]; + +// Make sure that the nested static allocation for `BAR` doesn't inherit `export_name`. +#[export_name = "BAR_"] +pub static mut BAR: &mut [i32] = &mut [42]; + +fn main() {} diff --git a/tests/ui/statics/nested_thread_local.rs b/tests/ui/statics/nested_thread_local.rs new file mode 100644 index 0000000000000..a512016335a00 --- /dev/null +++ b/tests/ui/statics/nested_thread_local.rs @@ -0,0 +1,14 @@ +// Check that we forbid nested statics in `thread_local` statics. + +#![feature(const_refs_to_cell)] +#![feature(thread_local)] + +#[thread_local] +static mut FOO: &u32 = { + //~^ ERROR: does not support implicit nested statics + // Prevent promotion (that would trigger on `&42` as an expression) + let x = 42; + &{ x } +}; + +fn main() {} diff --git a/tests/ui/statics/nested_thread_local.stderr b/tests/ui/statics/nested_thread_local.stderr new file mode 100644 index 0000000000000..30c742626fa96 --- /dev/null +++ b/tests/ui/statics/nested_thread_local.stderr @@ -0,0 +1,8 @@ +error: #[thread_local] does not support implicit nested statics, please create explicit static items and refer to them instead + --> $DIR/nested_thread_local.rs:7:1 + | +LL | static mut FOO: &u32 = { + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/structs-enums/type-sizes.rs b/tests/ui/structs-enums/type-sizes.rs index 66f663ce0776c..50491d5ef3eb6 100644 --- a/tests/ui/structs-enums/type-sizes.rs +++ b/tests/ui/structs-enums/type-sizes.rs @@ -4,7 +4,7 @@ #![allow(dead_code)] #![feature(generic_nonzero)] #![feature(never_type)] -#![feature(pointer_is_aligned)] +#![feature(pointer_is_aligned_to)] #![feature(strict_provenance)] use std::mem::size_of; diff --git a/tests/ui/thir-print/thir-tree-match.stdout b/tests/ui/thir-print/thir-tree-match.stdout index a17592fd2521f..b248d2a82684f 100644 --- a/tests/ui/thir-print/thir-tree-match.stdout +++ b/tests/ui/thir-print/thir-tree-match.stdout @@ -11,9 +11,8 @@ params: [ span: $DIR/thir-tree-match.rs:15:14: 15:17 (#0) kind: PatKind { Binding { - mutability: Not name: "foo" - mode: ByValue + mode: BindingAnnotation(No, Not) var: LocalVarId(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).2)) ty: Foo is_primary: true diff --git a/tests/ui/traits/stack-error-order-dependence-2.rs b/tests/ui/traits/stack-error-order-dependence-2.rs new file mode 100644 index 0000000000000..323685aa15bb5 --- /dev/null +++ b/tests/ui/traits/stack-error-order-dependence-2.rs @@ -0,0 +1,24 @@ +//@ check-pass +// Regression test for . +// This time EXCEPT without `dyn` builtin bounds :^) + +pub trait Trait: Supertrait {} + +trait Impossible {} +impl Trait for F {} + +pub trait Supertrait {} + +impl Supertrait for T {} + +fn needs_supertrait() {} +fn needs_trait() {} + +struct A; +impl Trait for A where A: Supertrait {} +impl Supertrait for A {} + +fn main() { + needs_supertrait::(); + needs_trait::(); +} diff --git a/tests/ui/traits/stack-error-order-dependence.rs b/tests/ui/traits/stack-error-order-dependence.rs new file mode 100644 index 0000000000000..037c292a542dc --- /dev/null +++ b/tests/ui/traits/stack-error-order-dependence.rs @@ -0,0 +1,19 @@ +//@ check-pass +// Regression test for . + +pub trait Trait: Supertrait {} + +trait Impossible {} +impl Trait for F {} + +pub trait Supertrait {} + +impl Supertrait for T {} + +fn needs_supertrait() {} +fn needs_trait() {} + +fn main() { + needs_supertrait::(); + needs_trait::(); +} diff --git a/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl-opaque.current.stderr b/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl-opaque.current.stderr new file mode 100644 index 0000000000000..c54a1c42baddf --- /dev/null +++ b/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl-opaque.current.stderr @@ -0,0 +1,17 @@ +error[E0308]: mismatched types + --> $DIR/illegal-upcast-from-impl-opaque.rs:26:5 + | +LL | type Foo = impl Sized; + | ---------- the found opaque type +LL | +LL | fn illegal(x: &dyn Sub) -> &dyn Super { + | ----------------------- expected `&dyn Super` because of return type +LL | x + | ^ expected trait `Super`, found trait `Sub` + | + = note: expected reference `&dyn Super` + found reference `&dyn Sub` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl-opaque.next.stderr b/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl-opaque.next.stderr new file mode 100644 index 0000000000000..3c2bc0b919065 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl-opaque.next.stderr @@ -0,0 +1,14 @@ +error: internal compiler error: error performing operation: query type op + --> $DIR/illegal-upcast-from-impl-opaque.rs:25:1 + | +LL | fn illegal(x: &dyn Sub) -> &dyn Super { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: + --> $DIR/illegal-upcast-from-impl-opaque.rs:25:1 + | +LL | fn illegal(x: &dyn Sub) -> &dyn Super { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +query stack during panic: +end of query stack diff --git a/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl-opaque.rs b/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl-opaque.rs new file mode 100644 index 0000000000000..f344474054a22 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl-opaque.rs @@ -0,0 +1,29 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@[next] failure-status: 101 +//@[next] known-bug: unknown +//@[next] normalize-stderr-test "note: .*\n\n" -> "" +//@[next] normalize-stderr-test "thread 'rustc' panicked.*\n.*\n" -> "" +//@[next] normalize-stderr-test "(error: internal compiler error: [^:]+):\d+:\d+: " -> "$1:LL:CC: " +//@[next] normalize-stderr-test "delayed at .*" -> "" +//@[next] rustc-env:RUST_BACKTRACE=0 + +#![feature(trait_upcasting, type_alias_impl_trait)] + +trait Super { + type Assoc; +} + +trait Sub: Super {} + +impl Super for T { + type Assoc = i32; +} + +type Foo = impl Sized; + +fn illegal(x: &dyn Sub) -> &dyn Super { + x //[current]~ mismatched types +} + +fn main() {} diff --git a/tests/ui/unpretty/hir-tree.rs b/tests/ui/unpretty/hir-tree.rs new file mode 100644 index 0000000000000..3388c60c42535 --- /dev/null +++ b/tests/ui/unpretty/hir-tree.rs @@ -0,0 +1,10 @@ +//@ build-pass +//@ compile-flags: -o - -Zunpretty=hir-tree +//@ check-stdout +//@ dont-check-compiler-stdout +//@ dont-check-compiler-stderr +//@ regex-error-pattern: Hello, Rustaceans! + +fn main() { + println!("Hello, Rustaceans!"); +} diff --git a/tests/ui/version-flags/version-info-flags.rs b/tests/ui/version-flags/version-info-flags.rs new file mode 100644 index 0000000000000..4da171b6b227e --- /dev/null +++ b/tests/ui/version-flags/version-info-flags.rs @@ -0,0 +1,10 @@ +// Check that rustc accepts various version info flags. +//@ dont-check-compiler-stdout +//@ revisions: version verbose-version long-verbose-version +//@ check-pass + +//@[version] compile-flags: -V +//@[verbose-version] compile-flags: -vV +//@[long-verbose-version] compile-flags: --version --verbose + +fn main() {} diff --git a/tests/ui/wf/wf-fn-def-check-sig-1.rs b/tests/ui/wf/wf-fn-def-check-sig-1.rs new file mode 100644 index 0000000000000..6d9e1f38f8d3e --- /dev/null +++ b/tests/ui/wf/wf-fn-def-check-sig-1.rs @@ -0,0 +1,44 @@ +// Regression test for #84533. + +use std::marker::PhantomData; + +fn foo<'b, 'a>() -> PhantomData<&'b &'a ()> { + PhantomData +} + +fn extend_lifetime<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T { + let f = foo::<'b, 'a>; + f.baz(x) + //~^ ERROR lifetime may not live long enough +} + +trait Foo<'a, 'b, T: ?Sized> { + fn baz(self, s: &'a T) -> &'b T; +} +impl<'a, 'b, R, F, T: ?Sized> Foo<'a, 'b, T> for F +where + F: Fn() -> R, + R: ProofForConversion<'a, 'b, T>, +{ + fn baz(self, s: &'a T) -> &'b T { + self().convert(s) + } +} + +trait ProofForConversion<'a, 'b, T: ?Sized> { + fn convert(self, s: &'a T) -> &'b T; +} +impl<'a, 'b, T: ?Sized> ProofForConversion<'a, 'b, T> for PhantomData<&'b &'a ()> { + fn convert(self, s: &'a T) -> &'b T { + s + } +} + +fn main() { + let d; + { + let x = "Hello World".to_string(); + d = extend_lifetime(&x); + } + println!("{}", d); +} diff --git a/tests/ui/wf/wf-fn-def-check-sig-1.stderr b/tests/ui/wf/wf-fn-def-check-sig-1.stderr new file mode 100644 index 0000000000000..a93449ad3c63e --- /dev/null +++ b/tests/ui/wf/wf-fn-def-check-sig-1.stderr @@ -0,0 +1,15 @@ +error: lifetime may not live long enough + --> $DIR/wf-fn-def-check-sig-1.rs:11:5 + | +LL | fn extend_lifetime<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | let f = foo::<'b, 'a>; +LL | f.baz(x) + | ^^^^^^^^ 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/tests/ui/wf/wf-fn-def-check-sig-2.rs b/tests/ui/wf/wf-fn-def-check-sig-2.rs new file mode 100644 index 0000000000000..51740dca8e9be --- /dev/null +++ b/tests/ui/wf/wf-fn-def-check-sig-2.rs @@ -0,0 +1,44 @@ +// Regression test for #84533 involving higher-ranked regions +// in the return type. +use std::marker::PhantomData; + +fn foo<'c, 'b, 'a>(_: &'c ()) -> (&'c (), PhantomData<&'b &'a ()>) { + (&(), PhantomData) +} + +fn extend_lifetime<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T { + let f = foo; + f.baz(x) + //~^ ERROR lifetime may not live long enough +} + +trait Foo<'a, 'b, T: ?Sized> { + fn baz(self, s: &'a T) -> &'b T; +} +impl<'a, 'b, R, F, T: ?Sized> Foo<'a, 'b, T> for F +where + F: for<'c> Fn(&'c ()) -> (&'c (), R), + R: ProofForConversion<'a, 'b, T>, +{ + fn baz(self, s: &'a T) -> &'b T { + self(&()).1.convert(s) + } +} + +trait ProofForConversion<'a, 'b, T: ?Sized> { + fn convert(self, s: &'a T) -> &'b T; +} +impl<'a, 'b, T: ?Sized> ProofForConversion<'a, 'b, T> for PhantomData<&'b &'a ()> { + fn convert(self, s: &'a T) -> &'b T { + s + } +} + +fn main() { + let d; + { + let x = "Hello World".to_string(); + d = extend_lifetime(&x); + } + println!("{}", d); +} diff --git a/tests/ui/wf/wf-fn-def-check-sig-2.stderr b/tests/ui/wf/wf-fn-def-check-sig-2.stderr new file mode 100644 index 0000000000000..404d3cc4513bc --- /dev/null +++ b/tests/ui/wf/wf-fn-def-check-sig-2.stderr @@ -0,0 +1,15 @@ +error: lifetime may not live long enough + --> $DIR/wf-fn-def-check-sig-2.rs:11:5 + | +LL | fn extend_lifetime<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | let f = foo; +LL | f.baz(x) + | ^^^^^^^^ 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 0df71c60d5454..3db0f7dc44392 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -469,11 +469,11 @@ message = "Some changes occurred in need_type_info.rs" cc = ["@lcnr"] [mentions."compiler/rustc_middle/src/ty/relate.rs"] -message = "Type relation code was changed" +message = "changes to the core type system" cc = ["@compiler-errors", "@lcnr"] [mentions."compiler/rustc_infer/src/infer/relate"] -message = "Type relation code was changed" +message = "changes to the core type system" cc = ["@compiler-errors", "@lcnr"] [mentions."compiler/rustc_middle/src/mir/interpret"] @@ -484,6 +484,10 @@ cc = ["@rust-lang/miri"] message = "Some changes occurred to MIR optimizations" cc = ["@rust-lang/wg-mir-opt"] +[mentions."compiler/rustc_trait_selection/src/traits/wf.rs"] +message = "changes to the core type system" +cc = ["@compiler-errors", "@lcnr"] + [mentions."compiler/rustc_trait_selection/src/traits/const_evaluatable.rs"] message = "Some changes occurred in `const_evaluatable.rs`" cc = ["@BoxyUwU"] @@ -788,6 +792,7 @@ compiler-team-contributors = [ "@Nadrieril", "@fmease", "@fee1-dead", + "@BoxyUwU", ] compiler = [ "compiler-team",

for AbsPath { + fn eq(&self, other: &P) -> bool { + self.0.as_std_path() == other.as_ref() + } +} + +impl AsRef for AbsPath { + fn as_ref(&self) -> &Utf8Path { + &self.0 + } +} impl AsRef for AbsPath { fn as_ref(&self) -> &Path { - &self.0 + self.0.as_ref() + } +} + +impl AsRef for AbsPath { + fn as_ref(&self) -> &OsStr { + self.0.as_ref() } } @@ -120,9 +180,9 @@ impl ToOwned for AbsPath { } } -impl<'a> TryFrom<&'a Path> for &'a AbsPath { - type Error = &'a Path; - fn try_from(path: &'a Path) -> Result<&'a AbsPath, &'a Path> { +impl<'a> TryFrom<&'a Utf8Path> for &'a AbsPath { + type Error = &'a Utf8Path; + fn try_from(path: &'a Utf8Path) -> Result<&'a AbsPath, &'a Utf8Path> { if !path.is_absolute() { return Err(path); } @@ -136,24 +196,24 @@ impl AbsPath { /// # Panics /// /// Panics if `path` is not absolute. - pub fn assert(path: &Path) -> &AbsPath { + pub fn assert(path: &Utf8Path) -> &AbsPath { assert!(path.is_absolute()); - unsafe { &*(path as *const Path as *const AbsPath) } + unsafe { &*(path as *const Utf8Path as *const AbsPath) } } - /// Equivalent of [`Path::parent`] for `AbsPath`. + /// Equivalent of [`Utf8Path::parent`] for `AbsPath`. pub fn parent(&self) -> Option<&AbsPath> { self.0.parent().map(AbsPath::assert) } - /// Equivalent of [`Path::join`] for `AbsPath` with an additional normalize step afterwards. - pub fn absolutize(&self, path: impl AsRef) -> AbsPathBuf { + /// Equivalent of [`Utf8Path::join`] for `AbsPath` with an additional normalize step afterwards. + pub fn absolutize(&self, path: impl AsRef) -> AbsPathBuf { self.join(path).normalize() } - /// Equivalent of [`Path::join`] for `AbsPath`. - pub fn join(&self, path: impl AsRef) -> AbsPathBuf { - self.as_ref().join(path).try_into().unwrap() + /// Equivalent of [`Utf8Path::join`] for `AbsPath`. + pub fn join(&self, path: impl AsRef) -> AbsPathBuf { + Utf8Path::join(self.as_ref(), path).try_into().unwrap() } /// Normalize the given path: @@ -172,7 +232,7 @@ impl AbsPath { AbsPathBuf(normalize_path(&self.0)) } - /// Equivalent of [`Path::to_path_buf`] for `AbsPath`. + /// Equivalent of [`Utf8Path::to_path_buf`] for `AbsPath`. pub fn to_path_buf(&self) -> AbsPathBuf { AbsPathBuf::try_from(self.0.to_path_buf()).unwrap() } @@ -181,7 +241,7 @@ impl AbsPath { panic!("We explicitly do not provide canonicalization API, as that is almost always a wrong solution, see #14430") } - /// Equivalent of [`Path::strip_prefix`] for `AbsPath`. + /// Equivalent of [`Utf8Path::strip_prefix`] for `AbsPath`. /// /// Returns a relative path. pub fn strip_prefix(&self, base: &AbsPath) -> Option<&RelPath> { @@ -195,57 +255,61 @@ impl AbsPath { } pub fn name_and_extension(&self) -> Option<(&str, Option<&str>)> { - Some(( - self.file_stem()?.to_str()?, - self.extension().and_then(|extension| extension.to_str()), - )) + Some((self.file_stem()?, self.extension())) } // region:delegate-methods - // Note that we deliberately don't implement `Deref` here. + // Note that we deliberately don't implement `Deref` here. // - // The problem with `Path` is that it directly exposes convenience IO-ing - // methods. For example, `Path::exists` delegates to `fs::metadata`. + // The problem with `Utf8Path` is that it directly exposes convenience IO-ing + // methods. For example, `Utf8Path::exists` delegates to `fs::metadata`. // // For `AbsPath`, we want to make sure that this is a POD type, and that all // IO goes via `fs`. That way, it becomes easier to mock IO when we need it. - pub fn file_name(&self) -> Option<&OsStr> { + pub fn file_name(&self) -> Option<&str> { self.0.file_name() } - pub fn extension(&self) -> Option<&OsStr> { + pub fn extension(&self) -> Option<&str> { self.0.extension() } - pub fn file_stem(&self) -> Option<&OsStr> { + pub fn file_stem(&self) -> Option<&str> { self.0.file_stem() } pub fn as_os_str(&self) -> &OsStr { self.0.as_os_str() } + pub fn as_str(&self) -> &str { + self.0.as_str() + } #[deprecated(note = "use Display instead")] - pub fn display(&self) -> std::path::Display<'_> { - self.0.display() + pub fn display(&self) -> ! { + unimplemented!() } #[deprecated(note = "use std::fs::metadata().is_ok() instead")] - pub fn exists(&self) -> bool { - self.0.exists() + pub fn exists(&self) -> ! { + unimplemented!() + } + + pub fn components(&self) -> Utf8Components<'_> { + self.0.components() } // endregion:delegate-methods } impl fmt::Display for AbsPath { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.0.display(), f) + fmt::Display::fmt(&self.0, f) } } -/// Wrapper around a relative [`PathBuf`]. +/// Wrapper around a relative [`Utf8PathBuf`]. #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] -pub struct RelPathBuf(PathBuf); +pub struct RelPathBuf(Utf8PathBuf); -impl From for PathBuf { - fn from(RelPathBuf(path_buf): RelPathBuf) -> PathBuf { +impl From for Utf8PathBuf { + fn from(RelPathBuf(path_buf): RelPathBuf) -> Utf8PathBuf { path_buf } } @@ -257,15 +321,21 @@ impl ops::Deref for RelPathBuf { } } +impl AsRef for RelPathBuf { + fn as_ref(&self) -> &Utf8Path { + self.0.as_path() + } +} + impl AsRef for RelPathBuf { fn as_ref(&self) -> &Path { - self.0.as_path() + self.0.as_ref() } } -impl TryFrom for RelPathBuf { - type Error = PathBuf; - fn try_from(path_buf: PathBuf) -> Result { +impl TryFrom for RelPathBuf { + type Error = Utf8PathBuf; + fn try_from(path_buf: Utf8PathBuf) -> Result { if !path_buf.is_relative() { return Err(path_buf); } @@ -274,65 +344,79 @@ impl TryFrom for RelPathBuf { } impl TryFrom<&str> for RelPathBuf { - type Error = PathBuf; - fn try_from(path: &str) -> Result { - RelPathBuf::try_from(PathBuf::from(path)) + type Error = Utf8PathBuf; + fn try_from(path: &str) -> Result { + RelPathBuf::try_from(Utf8PathBuf::from(path)) } } impl RelPathBuf { /// Coerces to a `RelPath` slice. /// - /// Equivalent of [`PathBuf::as_path`] for `RelPathBuf`. + /// Equivalent of [`Utf8PathBuf::as_path`] for `RelPathBuf`. pub fn as_path(&self) -> &RelPath { RelPath::new_unchecked(self.0.as_path()) } } -/// Wrapper around a relative [`Path`]. +/// Wrapper around a relative [`Utf8Path`]. #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] #[repr(transparent)] -pub struct RelPath(Path); +pub struct RelPath(Utf8Path); + +impl AsRef for RelPath { + fn as_ref(&self) -> &Utf8Path { + &self.0 + } +} impl AsRef for RelPath { fn as_ref(&self) -> &Path { - &self.0 + self.0.as_ref() } } impl RelPath { /// Creates a new `RelPath` from `path`, without checking if it is relative. - pub fn new_unchecked(path: &Path) -> &RelPath { - unsafe { &*(path as *const Path as *const RelPath) } + pub fn new_unchecked(path: &Utf8Path) -> &RelPath { + unsafe { &*(path as *const Utf8Path as *const RelPath) } } - /// Equivalent of [`Path::to_path_buf`] for `RelPath`. + /// Equivalent of [`Utf8Path::to_path_buf`] for `RelPath`. pub fn to_path_buf(&self) -> RelPathBuf { RelPathBuf::try_from(self.0.to_path_buf()).unwrap() } + + pub fn as_utf8_path(&self) -> &Utf8Path { + self.as_ref() + } + + pub fn as_str(&self) -> &str { + self.0.as_str() + } } /// Taken from -fn normalize_path(path: &Path) -> PathBuf { +fn normalize_path(path: &Utf8Path) -> Utf8PathBuf { let mut components = path.components().peekable(); - let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().copied() { + let mut ret = if let Some(c @ Utf8Component::Prefix(..)) = components.peek().copied() { components.next(); - PathBuf::from(c.as_os_str()) + Utf8PathBuf::from(c.as_str()) } else { - PathBuf::new() + Utf8PathBuf::new() }; for component in components { match component { - Component::Prefix(..) => unreachable!(), - Component::RootDir => { - ret.push(component.as_os_str()); + Utf8Component::Prefix(..) => unreachable!(), + Utf8Component::RootDir => { + ret.push(component.as_str()); } - Component::CurDir => {} - Component::ParentDir => { + Utf8Component::CurDir => {} + Utf8Component::ParentDir => { ret.pop(); } - Component::Normal(c) => { + Utf8Component::Normal(c) => { ret.push(c); } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml index 978ad155609d5..f30f6a0f23b91 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml @@ -23,7 +23,7 @@ snap = "1.1.0" indexmap = "2.1.0" # local deps -paths.workspace = true +paths = { workspace = true, features = ["serde1"] } tt.workspace = true stdx.workspace = true text-size.workspace = true 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 6b16711a8d87b..fd491644648a2 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 @@ -35,8 +35,11 @@ pub use version::{read_dylib_info, read_version, RustCInfo}; #[derive(Copy, Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] pub enum ProcMacroKind { CustomDerive, - FuncLike, Attr, + // This used to be called FuncLike, so that's what the server expects currently. + #[serde(alias = "bang")] + #[serde(rename(serialize = "FuncLike", deserialize = "FuncLike"))] + Bang, } /// A handle to an external process which load dylibs with macros (.so or .dll) diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs index aa5aff455fd80..ad0e1f187b622 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs @@ -1,11 +1,9 @@ //! Defines messages for cross-process message passing based on `ndjson` wire protocol pub(crate) mod flat; -use std::{ - io::{self, BufRead, Write}, - path::PathBuf, -}; +use std::io::{self, BufRead, Write}; +use paths::Utf8PathBuf; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use crate::ProcMacroKind; @@ -27,7 +25,7 @@ pub const CURRENT_API_VERSION: u32 = RUST_ANALYZER_SPAN_SUPPORT; #[derive(Debug, Serialize, Deserialize)] pub enum Request { /// Since [`NO_VERSION_CHECK_VERSION`] - ListMacros { dylib_path: PathBuf }, + ListMacros { dylib_path: Utf8PathBuf }, /// Since [`NO_VERSION_CHECK_VERSION`] ExpandMacro(Box), /// Since [`VERSION_CHECK_VERSION`] @@ -89,7 +87,7 @@ pub struct ExpandMacro { /// Possible attributes for the attribute-like macros. pub attributes: Option, - pub lib: PathBuf, + pub lib: Utf8PathBuf, /// Environment variables to set during macro expansion. pub env: Vec<(String, String)>, @@ -273,7 +271,7 @@ mod tests { macro_body: FlatTree::new(&tt, CURRENT_API_VERSION, &mut span_data_table), macro_name: Default::default(), attributes: None, - lib: std::env::current_dir().unwrap(), + lib: Utf8PathBuf::from_path_buf(std::env::current_dir().unwrap()).unwrap(), env: Default::default(), current_dir: Default::default(), has_global_spans: ExpnGlobals { 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/msg/flat.rs index caf9e237fdd7d..99cdbd930e1c6 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs @@ -88,8 +88,6 @@ impl std::fmt::Debug for TokenId { } } -impl tt::Span for TokenId {} - #[derive(Serialize, Deserialize, Debug)] pub struct FlatTree { subtree: Vec, 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 72f95643c8b5e..35d48a155433f 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 @@ -175,7 +175,7 @@ fn mk_child( env: &FxHashMap, null_stderr: bool, ) -> io::Result { - let mut cmd = Command::new(path.as_os_str()); + let mut cmd = Command::new(path); cmd.envs(env) .env("RUST_ANALYZER_INTERNALS_DO_NOT_USE", "this is unstable") .stdin(Stdio::piped()) @@ -183,7 +183,7 @@ fn mk_child( .stderr(if null_stderr { Stdio::null() } else { Stdio::inherit() }); if cfg!(windows) { let mut path_var = std::ffi::OsString::new(); - path_var.push(path.parent().unwrap().parent().unwrap().as_os_str()); + path_var.push(path.parent().unwrap().parent().unwrap()); path_var.push("\\bin;"); path_var.push(std::env::var_os("PATH").unwrap_or_default()); cmd.env("PATH", path_var); 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 52b4cced5f584..22c34ff1678d8 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 @@ -1,16 +1,11 @@ //! Handles dynamic library loading for proc macro -use std::{ - fmt, - fs::File, - io, - path::{Path, PathBuf}, -}; +use std::{fmt, fs::File, io}; use libloading::Library; use memmap2::Mmap; use object::Object; -use paths::AbsPath; +use paths::{AbsPath, Utf8Path, Utf8PathBuf}; use proc_macro::bridge; use proc_macro_api::{read_dylib_info, ProcMacroKind}; @@ -26,7 +21,7 @@ fn is_derive_registrar_symbol(symbol: &str) -> bool { symbol.contains(NEW_REGISTRAR_SYMBOL) } -fn find_registrar_symbol(file: &Path) -> io::Result> { +fn find_registrar_symbol(file: &Utf8Path) -> io::Result> { let file = File::open(file)?; let buffer = unsafe { Mmap::map(&file)? }; @@ -62,12 +57,12 @@ fn find_registrar_symbol(file: &Path) -> io::Result> { /// /// It seems that on Windows that behaviour is default, so we do nothing in that case. #[cfg(windows)] -fn load_library(file: &Path) -> Result { +fn load_library(file: &Utf8Path) -> Result { unsafe { Library::new(file) } } #[cfg(unix)] -fn load_library(file: &Path) -> Result { +fn load_library(file: &Utf8Path) -> Result { use libloading::os::unix::Library as UnixLibrary; use std::os::raw::c_int; @@ -116,14 +111,14 @@ struct ProcMacroLibraryLibloading { } impl ProcMacroLibraryLibloading { - fn open(file: &Path) -> Result { + fn open(file: &Utf8Path) -> Result { let symbol_name = find_registrar_symbol(file)?.ok_or_else(|| { - invalid_data_err(format!("Cannot find registrar symbol in file {}", file.display())) + invalid_data_err(format!("Cannot find registrar symbol in file {file}")) })?; - let abs_file: &AbsPath = file.try_into().map_err(|_| { - invalid_data_err(format!("expected an absolute path, got {}", file.display())) - })?; + let abs_file: &AbsPath = file + .try_into() + .map_err(|_| invalid_data_err(format!("expected an absolute path, got {file}")))?; let version_info = read_dylib_info(abs_file)?; let lib = load_library(file).map_err(invalid_data_err)?; @@ -138,10 +133,10 @@ pub struct Expander { } impl Expander { - pub fn new(lib: &Path) -> Result { + pub fn new(lib: &Utf8Path) -> Result { // Some libraries for dynamic loading require canonicalized path even when it is // already absolute - let lib = lib.canonicalize()?; + let lib = lib.canonicalize_utf8()?; let lib = ensure_file_with_lock_free_access(&lib)?; @@ -176,30 +171,26 @@ impl Expander { /// Copy the dylib to temp directory to prevent locking in Windows #[cfg(windows)] -fn ensure_file_with_lock_free_access(path: &Path) -> io::Result { +fn ensure_file_with_lock_free_access(path: &Utf8Path) -> io::Result { use std::collections::hash_map::RandomState; - use std::ffi::OsString; use std::hash::{BuildHasher, Hasher}; if std::env::var("RA_DONT_COPY_PROC_MACRO_DLL").is_ok() { return Ok(path.to_path_buf()); } - let mut to = std::env::temp_dir(); + let mut to = Utf8PathBuf::from_path_buf(std::env::temp_dir()).unwrap(); let file_name = path.file_name().ok_or_else(|| { - io::Error::new( - io::ErrorKind::InvalidInput, - format!("File path is invalid: {}", path.display()), - ) + io::Error::new(io::ErrorKind::InvalidInput, format!("File path is invalid: {path}")) })?; // Generate a unique number by abusing `HashMap`'s hasher. // Maybe this will also "inspire" a libs team member to finally put `rand` in libstd. let t = RandomState::new().build_hasher().finish(); - let mut unique_name = OsString::from(t.to_string()); - unique_name.push(file_name); + let mut unique_name = t.to_string(); + unique_name.push_str(file_name); to.push(unique_name); std::fs::copy(path, &to).unwrap(); @@ -207,6 +198,6 @@ fn ensure_file_with_lock_free_access(path: &Path) -> io::Result { } #[cfg(unix)] -fn ensure_file_with_lock_free_access(path: &Path) -> io::Result { - Ok(path.to_path_buf()) +fn ensure_file_with_lock_free_access(path: &Utf8Path) -> io::Result { + Ok(path.to_owned()) } 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 831632c64c0a2..2472c1e3119c1 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 @@ -33,12 +33,11 @@ use std::{ collections::{hash_map::Entry, HashMap}, env, ffi::OsString, - fs, - path::{Path, PathBuf}, - thread, + fs, thread, time::SystemTime, }; +use paths::{Utf8Path, Utf8PathBuf}; use proc_macro_api::{ msg::{ self, deserialize_span_data_index_map, serialize_span_data_index_map, ExpnGlobals, @@ -53,7 +52,7 @@ use crate::server::TokenStream; // see `build.rs` include!(concat!(env!("OUT_DIR"), "/rustc_version.rs")); -trait ProcMacroSrvSpan: tt::Span { +trait ProcMacroSrvSpan: Copy { type Server: proc_macro::bridge::server::Server>; fn make_server(call_site: Self, def_site: Self, mixed_site: Self) -> Self::Server; } @@ -81,7 +80,7 @@ impl ProcMacroSrvSpan for Span { #[derive(Default)] pub struct ProcMacroSrv { - expanders: HashMap<(PathBuf, SystemTime), dylib::Expander>, + expanders: HashMap<(Utf8PathBuf, SystemTime), dylib::Expander>, span_mode: SpanMode, } @@ -149,23 +148,22 @@ impl ProcMacroSrv { pub fn list_macros( &mut self, - dylib_path: &Path, + dylib_path: &Utf8Path, ) -> Result, String> { let expander = self.expander(dylib_path)?; Ok(expander.list_macros()) } - fn expander(&mut self, path: &Path) -> Result<&dylib::Expander, String> { + fn expander(&mut self, path: &Utf8Path) -> Result<&dylib::Expander, String> { let time = fs::metadata(path) .and_then(|it| it.modified()) - .map_err(|err| format!("Failed to get file metadata for {}: {err}", path.display()))?; + .map_err(|err| format!("Failed to get file metadata for {path}: {err}",))?; Ok(match self.expanders.entry((path.to_path_buf(), time)) { - Entry::Vacant(v) => { - v.insert(dylib::Expander::new(path).map_err(|err| { - format!("Cannot create expander for {}: {err}", path.display()) - })?) - } + Entry::Vacant(v) => v.insert( + dylib::Expander::new(path) + .map_err(|err| format!("Cannot create expander for {path}: {err}",))?, + ), Entry::Occupied(e) => e.into_mut(), }) } @@ -306,6 +304,6 @@ impl Drop for EnvSnapshot { mod tests; #[cfg(test)] -pub fn proc_macro_test_dylib_path() -> std::path::PathBuf { +pub fn proc_macro_test_dylib_path() -> paths::Utf8PathBuf { proc_macro_test::PROC_MACRO_TEST_LOCATION.into() } 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 686d5b0438aa9..631fd84aa248a 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 @@ -108,7 +108,7 @@ impl ProcMacros { (trait_name.to_string(), ProcMacroKind::CustomDerive) } bridge::client::ProcMacro::Bang { name, .. } => { - (name.to_string(), ProcMacroKind::FuncLike) + (name.to_string(), ProcMacroKind::Bang) } bridge::client::ProcMacro::Attr { name, .. } => { (name.to_string(), ProcMacroKind::Attr) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/token_stream.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/token_stream.rs index 408db60e872be..b1a448427c603 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/token_stream.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/token_stream.rs @@ -101,6 +101,8 @@ pub(super) struct TokenStreamBuilder { /// pub(super)lic implementation details for the `TokenStream` type, such as iterators. pub(super) mod token_stream { + use core::fmt; + use super::{TokenStream, TokenTree}; /// An iterator over `TokenStream`'s `TokenTree`s. @@ -122,7 +124,7 @@ pub(super) mod token_stream { /// /// NOTE: some errors may cause panics instead of returning `LexError`. We reserve the right to /// change these errors into `LexError`s later. - impl TokenStream { + impl TokenStream { pub(crate) fn from_str(src: &str, call_site: S) -> Result, String> { let subtree = mbe::parse_to_token_tree_static_span(call_site, src).ok_or("lexing error")?; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs index 11b008fc0b4bc..6334282538089 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs @@ -254,14 +254,14 @@ fn list_test_macros() { let res = list().join("\n"); expect![[r#" - fn_like_noop [FuncLike] - fn_like_panic [FuncLike] - fn_like_error [FuncLike] - fn_like_clone_tokens [FuncLike] - fn_like_mk_literals [FuncLike] - fn_like_mk_idents [FuncLike] - fn_like_span_join [FuncLike] - fn_like_span_ops [FuncLike] + fn_like_noop [Bang] + fn_like_panic [Bang] + fn_like_error [Bang] + fn_like_clone_tokens [Bang] + fn_like_mk_literals [Bang] + fn_like_mk_idents [Bang] + fn_like_span_join [Bang] + fn_like_span_ops [Bang] attr_noop [Attr] attr_panic [Attr] attr_error [Attr] diff --git a/src/tools/rust-analyzer/crates/project-model/Cargo.toml b/src/tools/rust-analyzer/crates/project-model/Cargo.toml index 924a4a89e216a..097ee1f75cd75 100644 --- a/src/tools/rust-analyzer/crates/project-model/Cargo.toml +++ b/src/tools/rust-analyzer/crates/project-model/Cargo.toml @@ -25,8 +25,9 @@ itertools.workspace = true # local deps base-db.workspace = true +span.workspace = true cfg.workspace = true -paths.workspace = true +paths = { workspace = true, features = ["serde1"] } stdx.workspace = true toolchain.workspace = true diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs index 709fc03717471..d40eb26063de8 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs @@ -77,7 +77,7 @@ impl WorkspaceBuildScripts { cmd.args(&config.extra_args); cmd.arg("--manifest-path"); - cmd.arg(workspace_root.join("Cargo.toml").as_os_str()); + cmd.arg(workspace_root.join("Cargo.toml")); if let Some(target_dir) = &config.target_dir { cmd.arg("--target-dir").arg(target_dir); @@ -354,16 +354,11 @@ impl WorkspaceBuildScripts { } // cargo_metadata crate returns default (empty) path for // older cargos, which is not absolute, so work around that. - let out_dir = mem::take(&mut message.out_dir).into_os_string(); - if !out_dir.is_empty() { - let out_dir = AbsPathBuf::assert(PathBuf::from(out_dir)); + let out_dir = mem::take(&mut message.out_dir); + if !out_dir.as_str().is_empty() { + let out_dir = AbsPathBuf::assert(out_dir); // inject_cargo_env(package, package_build_data); - // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() - if let Some(out_dir) = - out_dir.as_os_str().to_str().map(|s| s.to_owned()) - { - data.envs.push(("OUT_DIR".to_owned(), out_dir)); - } + data.envs.push(("OUT_DIR".to_owned(), out_dir.as_str().to_owned())); data.out_dir = Some(out_dir); data.cfgs = cfgs; } @@ -377,8 +372,8 @@ impl WorkspaceBuildScripts { if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name)) { - let filename = AbsPathBuf::assert(PathBuf::from(&filename)); - data.proc_macro_dylib_path = Some(filename); + let filename = AbsPath::assert(filename); + data.proc_macro_dylib_path = Some(filename.to_owned()); } } }); 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 53b41ea1e87b6..51c1b094f7b37 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 @@ -1,17 +1,16 @@ //! See [`CargoWorkspace`]. use std::ops; -use std::path::PathBuf; use std::str::from_utf8; use anyhow::Context; -use base_db::Edition; use cargo_metadata::{CargoOpt, MetadataCommand}; use la_arena::{Arena, Idx}; -use paths::{AbsPath, AbsPathBuf}; +use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::{FxHashMap, FxHashSet}; use serde::Deserialize; use serde_json::from_value; +use span::Edition; use toolchain::Tool; use crate::{utf8_stdout, InvocationLocation, ManifestPath, Sysroot}; @@ -100,7 +99,7 @@ pub struct CargoConfig { pub invocation_strategy: InvocationStrategy, pub invocation_location: InvocationLocation, /// Optional path to use instead of `target` when building - pub target_dir: Option, + pub target_dir: Option, } pub type Package = Idx; @@ -262,7 +261,7 @@ impl CargoWorkspace { } } } - meta.current_dir(current_dir.as_os_str()); + meta.current_dir(current_dir); let mut other_options = vec![]; // cargo metadata only supports a subset of flags of what cargo usually accepts, and usually @@ -351,7 +350,7 @@ impl CargoWorkspace { id: id.repr.clone(), name, version, - manifest: AbsPathBuf::assert(manifest_path.into()).try_into().unwrap(), + manifest: AbsPathBuf::assert(manifest_path).try_into().unwrap(), targets: Vec::new(), is_local, is_member, @@ -370,7 +369,7 @@ impl CargoWorkspace { let tgt = targets.alloc(TargetData { package: pkg, name, - root: AbsPathBuf::assert(src_path.into()), + root: AbsPathBuf::assert(src_path), kind: TargetKind::new(&kind), required_features, }); @@ -393,11 +392,9 @@ impl CargoWorkspace { packages[source].active_features.extend(node.features); } - let workspace_root = - AbsPathBuf::assert(PathBuf::from(meta.workspace_root.into_os_string())); + let workspace_root = AbsPathBuf::assert(meta.workspace_root); - let target_directory = - AbsPathBuf::assert(PathBuf::from(meta.target_directory.into_os_string())); + let target_directory = AbsPathBuf::assert(meta.target_directory); CargoWorkspace { packages, targets, workspace_root, target_directory } } @@ -409,7 +406,7 @@ impl CargoWorkspace { pub fn target_by_root(&self, root: &AbsPath) -> Option { self.packages() .filter(|&pkg| self[pkg].is_member) - .find_map(|pkg| self[pkg].targets.iter().find(|&&it| &self[it].root == root)) + .find_map(|pkg| self[pkg].targets.iter().find(|&&it| self[it].root == root)) .copied() } 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 5b91f5d805893..28696aa327759 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/lib.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/lib.rs @@ -127,8 +127,8 @@ impl ProjectManifest { .filter_map(Result::ok) .map(|it| it.path().join("Cargo.toml")) .filter(|it| it.exists()) - .map(AbsPathBuf::assert) - .filter_map(|it| it.try_into().ok()) + .map(AbsPathBuf::try_from) + .filter_map(|it| it.ok()?.try_into().ok()) .collect() } } diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs index fba0aaa8ce9f4..512588cc8f8f3 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs @@ -49,12 +49,12 @@ //! user explores them belongs to that extension (it's totally valid to change //! rust-project.json over time via configuration request!) -use base_db::{CrateDisplayName, CrateId, CrateName, Dependency, Edition}; +use base_db::{CrateDisplayName, CrateId, CrateName, Dependency}; use la_arena::RawIdx; -use paths::{AbsPath, AbsPathBuf}; +use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::FxHashMap; use serde::{de, Deserialize}; -use std::path::PathBuf; +use span::Edition; use crate::cfg_flag::CfgFlag; @@ -113,7 +113,7 @@ impl ProjectJson { .unwrap_or_else(|| root_module.starts_with(base)); let (include, exclude) = match crate_data.source { Some(src) => { - let absolutize = |dirs: Vec| { + let absolutize = |dirs: Vec| { dirs.into_iter().map(absolutize_on_base).collect::>() }; (absolutize(src.include_dirs), absolutize(src.exclude_dirs)) @@ -176,15 +176,15 @@ impl ProjectJson { #[derive(Deserialize, Debug, Clone)] pub struct ProjectJsonData { - sysroot: Option, - sysroot_src: Option, + sysroot: Option, + sysroot_src: Option, crates: Vec, } #[derive(Deserialize, Debug, Clone)] struct CrateData { display_name: Option, - root_module: PathBuf, + root_module: Utf8PathBuf, edition: EditionData, #[serde(default)] version: Option, @@ -194,7 +194,7 @@ struct CrateData { target: Option, #[serde(default)] env: FxHashMap, - proc_macro_dylib_path: Option, + proc_macro_dylib_path: Option, is_workspace_member: Option, source: Option, #[serde(default)] @@ -238,8 +238,8 @@ struct DepData { #[derive(Deserialize, Debug, Clone)] struct CrateSource { - include_dirs: Vec, - exclude_dirs: Vec, + include_dirs: Vec, + exclude_dirs: Vec, } fn deserialize_crate_name<'de, D>(de: D) -> std::result::Result 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 3127bae8b0c9e..1142d6243d29a 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs @@ -4,13 +4,13 @@ //! 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, iter, ops, path::PathBuf, process::Command, sync::Arc}; +use std::{env, fs, iter, ops, process::Command, sync::Arc}; use anyhow::{format_err, Result}; use base_db::CrateName; use itertools::Itertools; use la_arena::{Arena, Idx}; -use paths::{AbsPath, AbsPathBuf}; +use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::FxHashMap; use toolchain::{probe_for_binary, Tool}; @@ -419,7 +419,7 @@ fn discover_sysroot_dir( rustc.current_dir(current_dir).args(["--print", "sysroot"]); tracing::debug!("Discovering sysroot by {:?}", rustc); let stdout = utf8_stdout(rustc)?; - Ok(AbsPathBuf::assert(PathBuf::from(stdout))) + Ok(AbsPathBuf::assert(Utf8PathBuf::from(stdout))) } fn discover_sysroot_src_dir(sysroot_path: &AbsPathBuf) -> Option { 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 b9b1b701f6d40..fc0b507b33280 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs @@ -1,12 +1,9 @@ -use std::{ - ops::Deref, - path::{Path, PathBuf}, -}; +use std::ops::Deref; use base_db::{CrateGraph, FileId, ProcMacroPaths}; use cfg::{CfgAtom, CfgDiff}; use expect_test::{expect_file, ExpectFile}; -use paths::{AbsPath, AbsPathBuf}; +use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf}; use rustc_hash::FxHashMap; use serde::de::DeserializeOwned; use triomphe::Arc; @@ -113,17 +110,16 @@ fn replace_root(s: &mut String, direction: bool) { fn replace_fake_sys_root(s: &mut String) { let fake_sysroot_path = get_test_path("fake-sysroot"); let fake_sysroot_path = if cfg!(windows) { - let normalized_path = - fake_sysroot_path.to_str().expect("expected str").replace('\\', r#"\\"#); + let normalized_path = fake_sysroot_path.as_str().replace('\\', r#"\\"#); format!(r#"{}\\"#, normalized_path) } else { - format!("{}/", fake_sysroot_path.to_str().expect("expected str")) + format!("{}/", fake_sysroot_path.as_str()) }; *s = s.replace(&fake_sysroot_path, "$FAKESYSROOT$") } -fn get_test_path(file: &str) -> PathBuf { - let base = PathBuf::from(env!("CARGO_MANIFEST_DIR")); +fn get_test_path(file: &str) -> Utf8PathBuf { + let base = Utf8PathBuf::from(env!("CARGO_MANIFEST_DIR")); base.join("test_data").join(file) } @@ -139,7 +135,7 @@ fn get_fake_sysroot() -> Sysroot { fn rooted_project_json(data: ProjectJsonData) -> ProjectJson { let mut root = "$ROOT$".to_owned(); replace_root(&mut root, true); - let path = Path::new(&root); + let path = Utf8Path::new(&root); let base = AbsPath::assert(path); ProjectJson::new(base, data) } @@ -268,7 +264,7 @@ fn smoke_test_real_sysroot_cargo() { let cargo_workspace = CargoWorkspace::new(meta); let sysroot = Ok(Sysroot::discover( - AbsPath::assert(Path::new(env!("CARGO_MANIFEST_DIR"))), + AbsPath::assert(Utf8Path::new(env!("CARGO_MANIFEST_DIR"))), &Default::default(), true, ) 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 1a138b17bad41..b8c5885108d3d 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -6,13 +6,14 @@ use std::{collections::VecDeque, fmt, fs, iter, str::FromStr, sync}; use anyhow::{format_err, Context}; use base_db::{ - CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Edition, Env, - FileId, LangCrateOrigin, ProcMacroPaths, TargetLayoutLoadResult, + CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Env, FileId, + LangCrateOrigin, ProcMacroPaths, TargetLayoutLoadResult, }; use cfg::{CfgAtom, CfgDiff, CfgOptions}; use paths::{AbsPath, AbsPathBuf}; use rustc_hash::{FxHashMap, FxHashSet}; use semver::Version; +use span::Edition; use stdx::always; use toolchain::Tool; use triomphe::Arc; @@ -718,19 +719,22 @@ impl ProjectWorkspace { ) -> (CrateGraph, ProcMacroPaths) { let _p = tracing::span!(tracing::Level::INFO, "ProjectWorkspace::to_crate_graph").entered(); - let (mut crate_graph, proc_macros) = match self { + let ((mut crate_graph, proc_macros), sysroot) = match self { ProjectWorkspace::Json { project, sysroot, rustc_cfg, toolchain: _, target_layout: _, - } => project_json_to_crate_graph( - rustc_cfg.clone(), - load, - project, - sysroot.as_ref().ok(), - extra_env, + } => ( + project_json_to_crate_graph( + rustc_cfg.clone(), + load, + project, + sysroot.as_ref().ok(), + extra_env, + ), + sysroot, ), ProjectWorkspace::Cargo { cargo, @@ -742,14 +746,17 @@ impl ProjectWorkspace { toolchain: _, target_layout: _, cargo_config_extra_env: _, - } => cargo_to_crate_graph( - load, - rustc.as_ref().map(|a| a.as_ref()).ok(), - cargo, - sysroot.as_ref().ok(), - rustc_cfg.clone(), - cfg_overrides, - build_scripts, + } => ( + cargo_to_crate_graph( + load, + rustc.as_ref().map(|a| a.as_ref()).ok(), + cargo, + sysroot.as_ref().ok(), + rustc_cfg.clone(), + cfg_overrides, + build_scripts, + ), + sysroot, ), ProjectWorkspace::DetachedFiles { files, @@ -757,11 +764,20 @@ impl ProjectWorkspace { rustc_cfg, toolchain: _, target_layout: _, - } => { - detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot.as_ref().ok()) - } + } => ( + detached_files_to_crate_graph( + rustc_cfg.clone(), + load, + files, + sysroot.as_ref().ok(), + ), + sysroot, + ), }; - if crate_graph.patch_cfg_if() { + + if matches!(sysroot.as_ref().map(|it| it.mode()), Ok(SysrootMode::Workspace(_))) + && crate_graph.patch_cfg_if() + { tracing::debug!("Patched std to depend on cfg-if") } else { tracing::debug!("Did not patch std to depend on cfg-if") @@ -1077,6 +1093,8 @@ fn cargo_to_crate_graph( } } + let mut delayed_dev_deps = vec![]; + // Now add a dep edge from all targets of upstream to the lib // target of downstream. for pkg in cargo.packages() { @@ -1091,11 +1109,31 @@ fn cargo_to_crate_graph( continue; } + // If the dependency is a dev-dependency with both crates being member libraries of + // the workspace we delay adding the edge. The reason can be read up on in + // https://github.com/rust-lang/rust-analyzer/issues/14167 + // but in short, such an edge is able to cause some form of cycle in the crate graph + // for normal dependencies. If we do run into a cycle like this, we want to prefer + // the non dev-dependency edge, and so the easiest way to do that is by adding the + // dev-dependency edges last. + if dep.kind == DepKind::Dev + && matches!(kind, TargetKind::Lib { .. }) + && cargo[dep.pkg].is_member + && cargo[pkg].is_member + { + delayed_dev_deps.push((from, name.clone(), to)); + continue; + } + add_dep(crate_graph, from, name.clone(), to) } } } + for (from, name, to) in delayed_dev_deps { + add_dep(crate_graph, from, name, to); + } + if has_private { // If the user provided a path to rustc sources, we add all the rustc_private crates // and create dependencies on them for the crates which opt-in to that @@ -1151,7 +1189,6 @@ fn detached_files_to_crate_graph( }; let display_name = detached_file .file_stem() - .and_then(|os_str| os_str.to_str()) .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_owned())); let detached_file_crate = crate_graph.add_crate_root( file_id, @@ -1228,6 +1265,7 @@ fn handle_rustc_crates( let kind @ TargetKind::Lib { is_proc_macro } = rustc_workspace[tgt].kind else { continue; }; + let pkg_crates = &mut rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new); if let Some(file_id) = load(&rustc_workspace[tgt].root) { let crate_id = add_target_crate_root( crate_graph, @@ -1246,7 +1284,7 @@ fn handle_rustc_crates( if let Some(proc_macro) = libproc_macro { add_proc_macro_dep(crate_graph, crate_id, proc_macro, is_proc_macro); } - rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id); + pkg_crates.push(crate_id); } } } @@ -1533,7 +1571,7 @@ fn inject_cargo_env(package: &PackageData, env: &mut Env) { // CARGO_BIN_NAME, CARGO_BIN_EXE_ let manifest_dir = package.manifest.parent(); - env.set("CARGO_MANIFEST_DIR", manifest_dir.as_os_str().to_string_lossy().into_owned()); + env.set("CARGO_MANIFEST_DIR", manifest_dir.as_str().to_owned()); // Not always right, but works for common cases. env.set("CARGO", "cargo".into()); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index e6984d6f41b7f..6d70124188d9c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml @@ -43,6 +43,7 @@ nohash-hasher.workspace = true always-assert = "0.2.0" walkdir = "2.3.2" semver.workspace = true +memchr = "2.7.1" cfg.workspace = true flycheck.workspace = true @@ -63,7 +64,7 @@ parser.workspace = true toolchain.workspace = true vfs-notify.workspace = true vfs.workspace = true -memchr = "2.7.1" +paths.workspace = true [target.'cfg(windows)'.dependencies] winapi = "0.3.9" 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 e747ec87b1c18..78920f3abace9 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 @@ -190,7 +190,7 @@ fn run_server() -> anyhow::Result<()> { Some(it) => it, None => { let cwd = env::current_dir()?; - AbsPathBuf::assert(cwd) + AbsPathBuf::assert_utf8(cwd) } }; 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 5c474908e7aed..fdd77199aa004 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 @@ -70,7 +70,7 @@ impl flags::AnalysisStats { let mut db_load_sw = self.stop_watch(); - let path = AbsPathBuf::assert(env::current_dir()?.join(&self.path)); + let path = AbsPathBuf::assert_utf8(env::current_dir()?.join(&self.path)); let manifest = ProjectManifest::discover_single(&path)?; let mut workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?; @@ -279,7 +279,8 @@ impl flags::AnalysisStats { let mut all = 0; let mut fail = 0; for &a in adts { - if db.generic_params(a.into()).iter().next().is_some() { + let generic_params = db.generic_params(a.into()); + if generic_params.iter().next().is_some() || generic_params.iter_lt().next().is_some() { // Data types with generics don't have layout. continue; } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs index bd2646126dcbf..79d6226debf99 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs @@ -16,7 +16,7 @@ impl flags::Diagnostics { let cargo_config = CargoConfig { sysroot: Some(RustLibSource::Discover), ..Default::default() }; let with_proc_macro_server = if let Some(p) = &self.proc_macro_srv { - let path = vfs::AbsPathBuf::assert(std::env::current_dir()?.join(p)); + let path = vfs::AbsPathBuf::assert_utf8(std::env::current_dir()?.join(p)); ProcMacroServerChoice::Explicit(path) } else { ProcMacroServerChoice::Sysroot 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 3f68c5d053b7b..b3b8ab9a40499 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 @@ -235,6 +235,7 @@ pub struct RunTests { #[derive(Debug)] pub struct RustcTests { pub rustc_repo: PathBuf, + pub filter: Option, } 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 f3f5ec1ebde2f..3ff9be7102fbc 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 @@ -283,7 +283,7 @@ impl flags::Lsif { with_proc_macro_server: ProcMacroServerChoice::Sysroot, prefill_caches: false, }; - let path = AbsPathBuf::assert(env::current_dir()?.join(self.path)); + let path = AbsPathBuf::assert_utf8(env::current_dir()?.join(self.path)); let manifest = ProjectManifest::discover_single(&path)?; let workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?; 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 84f2e6008746b..eeec13a14bee5 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 @@ -76,7 +76,7 @@ impl Tester { ); let workspace = ProjectWorkspace::DetachedFiles { - files: vec![tmp_file.clone()], + files: vec![tmp_file], sysroot, rustc_cfg: vec![], toolchain: None, 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 1061a433a58f9..aef2c1be22494 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 @@ -27,7 +27,8 @@ impl flags::Scip { with_proc_macro_server: ProcMacroServerChoice::Sysroot, prefill_caches: true, }; - let root = vfs::AbsPathBuf::assert(std::env::current_dir()?.join(&self.path)).normalize(); + let root = + vfs::AbsPathBuf::assert_utf8(std::env::current_dir()?.join(&self.path)).normalize(); let mut config = crate::config::Config::new( root.clone(), @@ -63,12 +64,7 @@ impl flags::Scip { special_fields: Default::default(), }) .into(), - project_root: format!( - "file://{}", - root.as_os_str() - .to_str() - .ok_or(anyhow::format_err!("Unable to normalize project_root path"))? - ), + project_root: format!("file://{root}"), text_document_encoding: scip_types::TextEncoding::UTF8.into(), special_fields: Default::default(), }; @@ -216,7 +212,7 @@ fn get_relative_filepath( rootpath: &vfs::AbsPathBuf, file_id: ide::FileId, ) -> Option { - Some(vfs.file_path(file_id).as_path()?.strip_prefix(rootpath)?.as_ref().to_str()?.to_owned()) + Some(vfs.file_path(file_id).as_path()?.strip_prefix(rootpath)?.as_str().to_owned()) } // SCIP Ranges have a (very large) optimization that ranges if they are on the same line 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 cbf1524659090..7475a8e6e6d96 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -7,11 +7,7 @@ //! configure the server itself, feature flags are passed into analysis, and //! tweak things like automatic insertion of `()` in completions. -use std::{ - fmt, iter, - ops::Not, - path::{Path, PathBuf}, -}; +use std::{fmt, iter, ops::Not}; use cfg::{CfgAtom, CfgDiff}; use flycheck::FlycheckConfig; @@ -27,6 +23,7 @@ use ide_db::{ }; use itertools::Itertools; use lsp_types::{ClientCapabilities, MarkupKind}; +use paths::{Utf8Path, Utf8PathBuf}; use project_model::{ CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectManifest, RustLibSource, }; @@ -327,7 +324,7 @@ config_data! { /// These directories will be ignored by rust-analyzer. They are /// relative to the workspace root, and globs are not supported. You may /// also need to add the folders to Code's `files.watcherExclude`. - files_excludeDirs: Vec = "[]", + files_excludeDirs: Vec = "[]", /// Controls file watching implementation. files_watcher: FilesWatcherDef = "\"client\"", @@ -378,6 +375,8 @@ config_data! { /// How to render the size information in a memory layout hover. hover_memoryLayout_size: Option = "\"both\"", + /// How many fields of a struct to display when hovering a struct. + hover_show_structFields: Option = "null", /// How many associated items of a trait to display when hovering a trait. hover_show_traitAssocItems: Option = "null", @@ -516,7 +515,7 @@ config_data! { /// This config takes a map of crate names with the exported proc-macro names to ignore as values. procMacro_ignored: FxHashMap, Box<[Box]>> = "{}", /// Internal config, path to proc-macro server executable. - procMacro_server: Option = "null", + procMacro_server: Option = "null", /// Exclude imports from find-all-references. references_excludeImports: bool = "false", @@ -864,7 +863,7 @@ impl Config { } let mut errors = Vec::new(); self.detached_files = - get_field::>(&mut json, &mut errors, "detachedFiles", None, "[]") + get_field::>(&mut json, &mut errors, "detachedFiles", None, "[]") .into_iter() .map(AbsPathBuf::assert) .collect(); @@ -956,7 +955,7 @@ impl Config { pub fn has_linked_projects(&self) -> bool { !self.data.linkedProjects.is_empty() } - pub fn linked_manifests(&self) -> impl Iterator + '_ { + pub fn linked_manifests(&self) -> impl Iterator + '_ { self.data.linkedProjects.iter().filter_map(|it| match it { ManifestOrProjectJson::Manifest(p) => Some(&**p), ManifestOrProjectJson::ProjectJson(_) => None, @@ -1014,6 +1013,17 @@ impl Config { ) } + pub fn did_change_watched_files_relative_pattern_support(&self) -> bool { + try_or_def!( + self.caps + .workspace + .as_ref()? + .did_change_watched_files + .as_ref()? + .relative_pattern_support? + ) + } + pub fn prefill_caches(&self) -> bool { self.data.cachePriming_enable } @@ -1410,9 +1420,11 @@ impl Config { } } - fn target_dir_from_config(&self) -> Option { + fn target_dir_from_config(&self) -> Option { self.data.cargo_targetDir.as_ref().and_then(|target_dir| match target_dir { - TargetDirectory::UseSubdirectory(true) => Some(PathBuf::from("target/rust-analyzer")), + TargetDirectory::UseSubdirectory(true) => { + Some(Utf8PathBuf::from("target/rust-analyzer")) + } TargetDirectory::UseSubdirectory(false) => None, TargetDirectory::Directory(dir) if dir.is_relative() => Some(dir.clone()), TargetDirectory::Directory(_) => None, @@ -1691,6 +1703,7 @@ impl Config { }, keywords: self.data.hover_documentation_keywords_enable, max_trait_assoc_items_count: self.data.hover_show_traitAssocItems, + max_struct_field_count: self.data.hover_show_structFields, } } @@ -1951,7 +1964,7 @@ where #[derive(Deserialize, Debug, Clone)] #[serde(untagged)] enum ManifestOrProjectJson { - Manifest(PathBuf), + Manifest(Utf8PathBuf), ProjectJson(ProjectJsonData), } @@ -2134,7 +2147,7 @@ pub enum MemoryLayoutHoverRenderKindDef { #[serde(untagged)] pub enum TargetDirectory { UseSubdirectory(bool), - Directory(PathBuf), + Directory(Utf8PathBuf), } macro_rules! _config_data { @@ -2263,7 +2276,7 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json "type": "array", "items": { "type": "string" }, }, - "Vec" => set! { + "Vec" => set! { "type": "array", "items": { "type": "string" }, }, @@ -2291,7 +2304,7 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json "Option" => set! { "type": ["null", "string"], }, - "Option" => set! { + "Option" => set! { "type": ["null", "string"], }, "Option" => set! { @@ -2774,7 +2787,7 @@ mod tests { .unwrap(); assert_eq!(config.data.cargo_targetDir, Some(TargetDirectory::UseSubdirectory(true))); assert!( - matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir == Some(PathBuf::from("target/rust-analyzer"))) + matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir == Some(Utf8PathBuf::from("target/rust-analyzer"))) ); } @@ -2793,10 +2806,10 @@ mod tests { .unwrap(); assert_eq!( config.data.cargo_targetDir, - Some(TargetDirectory::Directory(PathBuf::from("other_folder"))) + Some(TargetDirectory::Directory(Utf8PathBuf::from("other_folder"))) ); assert!( - matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir == Some(PathBuf::from("other_folder"))) + matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir == Some(Utf8PathBuf::from("other_folder"))) ); } } 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 6e6cc53c251c4..7c4deac93f21f 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 @@ -519,14 +519,13 @@ fn clippy_code_description(code: Option<&str>) -> Option anyhow::Result<()> { - for change in params.changes { + for change in params.changes.iter().unique_by(|&it| &it.uri) { if let Ok(path) = from_proto::abs_path(&change.uri) { state.loader.handle.invalidate(path); } 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 1d98457add330..77692ed3ae767 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 @@ -4,7 +4,6 @@ use std::{ fs, io::Write as _, - path::PathBuf, process::{self, Stdio}, }; @@ -12,8 +11,8 @@ use anyhow::Context; use ide::{ AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, FilePosition, FileRange, - HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query, RangeInfo, RangeLimit, - ReferenceCategory, Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit, + HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query, RangeInfo, ReferenceCategory, + Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit, }; use ide_db::SymbolKind; use itertools::Itertools; @@ -27,6 +26,7 @@ use lsp_types::{ SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit, }; +use paths::Utf8PathBuf; use project_model::{ManifestPath, ProjectWorkspace, TargetKind}; use serde_json::json; use stdx::{format_to, never}; @@ -238,9 +238,12 @@ pub(crate) fn handle_discover_test( let (tests, scope) = match params.test_id { Some(id) => { let crate_id = id.split_once("::").map(|it| it.0).unwrap_or(&id); - (snap.analysis.discover_tests_in_crate_by_test_id(crate_id)?, vec![crate_id.to_owned()]) + ( + snap.analysis.discover_tests_in_crate_by_test_id(crate_id)?, + Some(vec![crate_id.to_owned()]), + ) } - None => (snap.analysis.discover_test_roots()?, vec![]), + None => (snap.analysis.discover_test_roots()?, None), }; for t in &tests { hack_recover_crate_name::insert_name(t.id.clone()); @@ -248,12 +251,13 @@ pub(crate) fn handle_discover_test( Ok(lsp_ext::DiscoverTestResults { tests: tests .into_iter() - .map(|t| { + .filter_map(|t| { let line_index = t.file.and_then(|f| snap.file_line_index(f).ok()); to_proto::test_item(&snap, t, line_index.as_ref()) }) .collect(), scope, + scope_file: None, }) } @@ -1465,7 +1469,7 @@ pub(crate) fn handle_inlay_hints( let inlay_hints_config = snap.config.inlay_hints(); Ok(Some( snap.analysis - .inlay_hints(&inlay_hints_config, file_id, Some(RangeLimit::Fixed(range)))? + .inlay_hints(&inlay_hints_config, file_id, Some(range))? .into_iter() .map(|it| { to_proto::inlay_hint( @@ -1499,10 +1503,11 @@ pub(crate) fn handle_inlay_hints_resolve( let hint_position = from_proto::offset(&line_index, original_hint.position)?; let mut forced_resolve_inlay_hints_config = snap.config.inlay_hints(); forced_resolve_inlay_hints_config.fields_to_resolve = InlayFieldsToResolve::empty(); - let resolve_hints = snap.analysis.inlay_hints( + let resolve_hints = snap.analysis.inlay_hints_resolve( &forced_resolve_inlay_hints_config, file_id, - Some(RangeLimit::NearestParent(hint_position)), + hint_position, + resolve_data.hash, )?; let mut resolved_hints = resolve_hints @@ -1542,7 +1547,7 @@ pub(crate) fn handle_call_hierarchy_prepare( let RangeInfo { range: _, info: navs } = nav_info; let res = navs .into_iter() - .filter(|it| it.kind == Some(SymbolKind::Function)) + .filter(|it| matches!(it.kind, Some(SymbolKind::Function | SymbolKind::Method))) .map(|it| to_proto::call_hierarchy_item(&snap, it)) .collect::>>()?; @@ -1736,8 +1741,8 @@ pub(crate) fn handle_open_docs( _ => (None, None), }; - let sysroot = sysroot.map(|p| p.root().as_os_str()); - let target_dir = cargo.map(|cargo| cargo.target_directory()).map(|p| p.as_os_str()); + let sysroot = sysroot.map(|p| p.root().as_str()); + let target_dir = cargo.map(|cargo| cargo.target_directory()).map(|p| p.as_str()); let Ok(remote_urls) = snap.analysis.external_docs(position, target_dir, sysroot) else { return if snap.config.local_docs() { @@ -2042,7 +2047,7 @@ fn run_rustfmt( cmd } RustfmtConfig::CustomCommand { command, args } => { - let cmd = PathBuf::from(&command); + let cmd = Utf8PathBuf::from(&command); let workspace = CargoTargetSpec::for_file(snap, file_id)?; let mut cmd = match workspace { Some(spec) => { 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 1c5a862c70356..2731e845f351e 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 @@ -52,7 +52,7 @@ fn integrated_highlighting_benchmark() { let file_id = { let file = workspace_to_load.join(file); - let path = VfsPath::from(AbsPathBuf::assert(file)); + let path = VfsPath::from(AbsPathBuf::assert_utf8(file)); vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {path}")) }; @@ -112,7 +112,7 @@ fn integrated_completion_benchmark() { let file_id = { let file = workspace_to_load.join(file); - let path = VfsPath::from(AbsPathBuf::assert(file)); + let path = VfsPath::from(AbsPathBuf::assert_utf8(file)); vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {path}")) }; @@ -274,7 +274,7 @@ fn integrated_diagnostics_benchmark() { let file_id = { let file = workspace_to_load.join(file); - let path = VfsPath::from(AbsPathBuf::assert(file)); + let path = VfsPath::from(AbsPathBuf::assert_utf8(file)); vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {path}")) }; 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 710ce7f8acbb1..eac982f1b27b2 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 @@ -194,7 +194,8 @@ pub struct TestItem { #[serde(rename_all = "camelCase")] pub struct DiscoverTestResults { pub tests: Vec, - pub scope: Vec, + pub scope: Option>, + pub scope_file: Option>, } pub enum DiscoverTest {} @@ -800,6 +801,7 @@ pub struct CompletionResolveData { #[derive(Debug, Serialize, Deserialize)] pub struct InlayHintResolveData { pub file_id: u32, + pub hash: u64, } #[derive(Debug, Serialize, Deserialize)] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs index c5081c4bea02c..3e00222b752d3 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs @@ -127,13 +127,14 @@ macro_rules! define_semantic_token_modifiers { define_semantic_token_modifiers![ standard { + ASYNC, DOCUMENTATION, DECLARATION, STATIC, DEFAULT_LIBRARY, } custom { - (ASYNC, "async"), + (ASSOCIATED, "associated"), (ATTRIBUTE_MODIFIER, "attribute"), (CALLABLE, "callable"), (CONSTANT, "constant"), 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 e77d0c13bf2d9..d8bb12528b984 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 @@ -1,7 +1,7 @@ //! Conversion of rust-analyzer specific types to lsp_types equivalents. use std::{ iter::once, - mem, path, + mem, sync::atomic::{AtomicU32, Ordering}, }; @@ -13,8 +13,9 @@ use ide::{ NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity, SignatureHelp, SnippetEdit, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize, }; -use ide_db::rust_doc::format_docs; +use ide_db::{rust_doc::format_docs, FxHasher}; use itertools::Itertools; +use paths::{Utf8Component, Utf8Prefix}; use semver::VersionReq; use serde_json::to_value; use vfs::AbsPath; @@ -52,6 +53,7 @@ pub(crate) fn range(line_index: &LineIndex, range: TextRange) -> lsp_types::Rang pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind { match symbol_kind { SymbolKind::Function => lsp_types::SymbolKind::FUNCTION, + SymbolKind::Method => lsp_types::SymbolKind::METHOD, SymbolKind::Struct => lsp_types::SymbolKind::STRUCT, SymbolKind::Enum => lsp_types::SymbolKind::ENUM, SymbolKind::Variant => lsp_types::SymbolKind::ENUM_MEMBER, @@ -122,12 +124,12 @@ pub(crate) fn completion_item_kind( CompletionItemKind::BuiltinType => lsp_types::CompletionItemKind::STRUCT, CompletionItemKind::InferredType => lsp_types::CompletionItemKind::SNIPPET, CompletionItemKind::Keyword => lsp_types::CompletionItemKind::KEYWORD, - CompletionItemKind::Method => lsp_types::CompletionItemKind::METHOD, CompletionItemKind::Snippet => lsp_types::CompletionItemKind::SNIPPET, CompletionItemKind::UnresolvedReference => lsp_types::CompletionItemKind::REFERENCE, CompletionItemKind::Expression => lsp_types::CompletionItemKind::SNIPPET, CompletionItemKind::SymbolKind(symbol) => match symbol { SymbolKind::Attribute => lsp_types::CompletionItemKind::FUNCTION, + SymbolKind::Method => lsp_types::CompletionItemKind::METHOD, SymbolKind::Const => lsp_types::CompletionItemKind::CONSTANT, SymbolKind::ConstParam => lsp_types::CompletionItemKind::TYPE_PARAMETER, SymbolKind::Derive => lsp_types::CompletionItemKind::FUNCTION, @@ -444,30 +446,42 @@ pub(crate) fn inlay_hint( fields_to_resolve: &InlayFieldsToResolve, line_index: &LineIndex, file_id: FileId, - inlay_hint: InlayHint, + mut inlay_hint: InlayHint, ) -> Cancellable { - let needs_resolve = inlay_hint.needs_resolve; - let (label, tooltip, mut something_to_resolve) = - inlay_hint_label(snap, fields_to_resolve, needs_resolve, inlay_hint.label)?; + let resolve_hash = inlay_hint.needs_resolve().then(|| { + std::hash::BuildHasher::hash_one( + &std::hash::BuildHasherDefault::::default(), + &inlay_hint, + ) + }); + let mut something_to_resolve = false; 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)) - && needs_resolve + && resolve_hash.is_some() && fields_to_resolve.resolve_text_edits { something_to_resolve |= inlay_hint.text_edit.is_some(); None } else { - inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it)) + inlay_hint.text_edit.take().map(|it| text_edit_vec(line_index, it)) }; - - let data = if needs_resolve && something_to_resolve { - Some(to_value(lsp_ext::InlayHintResolveData { file_id: file_id.index() }).unwrap()) - } else { - None + let (label, tooltip) = inlay_hint_label( + snap, + fields_to_resolve, + &mut something_to_resolve, + resolve_hash.is_some(), + inlay_hint.label, + )?; + + let data = match resolve_hash { + Some(hash) if something_to_resolve => Some( + to_value(lsp_ext::InlayHintResolveData { file_id: file_id.index(), hash }).unwrap(), + ), + _ => None, }; Ok(lsp_types::InlayHint { @@ -492,15 +506,15 @@ pub(crate) fn inlay_hint( fn inlay_hint_label( snap: &GlobalStateSnapshot, fields_to_resolve: &InlayFieldsToResolve, + something_to_resolve: &mut bool, needs_resolve: bool, mut label: InlayHintLabel, -) -> Cancellable<(lsp_types::InlayHintLabel, Option, bool)> { - let mut something_to_resolve = false; +) -> Cancellable<(lsp_types::InlayHintLabel, Option)> { let (label, tooltip) = match &*label.parts { [InlayHintLabelPart { linked_location: None, .. }] => { let InlayHintLabelPart { text, tooltip, .. } = label.parts.pop().unwrap(); let hint_tooltip = if needs_resolve && fields_to_resolve.resolve_hint_tooltip { - something_to_resolve |= tooltip.is_some(); + *something_to_resolve |= tooltip.is_some(); None } else { match tooltip { @@ -524,7 +538,7 @@ fn inlay_hint_label( .into_iter() .map(|part| { let tooltip = if needs_resolve && fields_to_resolve.resolve_label_tooltip { - something_to_resolve |= part.tooltip.is_some(); + *something_to_resolve |= part.tooltip.is_some(); None } else { match part.tooltip { @@ -543,7 +557,7 @@ fn inlay_hint_label( } }; let location = if needs_resolve && fields_to_resolve.resolve_label_location { - something_to_resolve |= part.linked_location.is_some(); + *something_to_resolve |= part.linked_location.is_some(); None } else { part.linked_location.map(|range| location(snap, range)).transpose()? @@ -559,7 +573,7 @@ fn inlay_hint_label( (lsp_types::InlayHintLabel::LabelParts(parts), None) } }; - Ok((label, tooltip, something_to_resolve)) + Ok((label, tooltip)) } static TOKEN_RESULT_COUNTER: AtomicU32 = AtomicU32::new(1); @@ -636,8 +650,7 @@ pub(crate) fn semantic_token_delta( fn semantic_token_type_and_modifiers( highlight: Highlight, ) -> (lsp_types::SemanticTokenType, semantic_tokens::ModifierSet) { - let mut mods = semantic_tokens::ModifierSet::default(); - let type_ = match highlight.tag { + let ty = match highlight.tag { HlTag::Symbol(symbol) => match symbol { SymbolKind::Attribute => semantic_tokens::DECORATOR, SymbolKind::Derive => semantic_tokens::DERIVE, @@ -653,22 +666,10 @@ fn semantic_token_type_and_modifiers( SymbolKind::SelfParam => semantic_tokens::SELF_KEYWORD, SymbolKind::SelfType => semantic_tokens::SELF_TYPE_KEYWORD, SymbolKind::Local => semantic_tokens::VARIABLE, - SymbolKind::Function => { - if highlight.mods.contains(HlMod::Associated) { - semantic_tokens::METHOD - } else { - semantic_tokens::FUNCTION - } - } - SymbolKind::Const => { - mods |= semantic_tokens::CONSTANT; - mods |= semantic_tokens::STATIC; - semantic_tokens::VARIABLE - } - SymbolKind::Static => { - mods |= semantic_tokens::STATIC; - semantic_tokens::VARIABLE - } + SymbolKind::Method => semantic_tokens::METHOD, + SymbolKind::Function => semantic_tokens::FUNCTION, + SymbolKind::Const => semantic_tokens::VARIABLE, + SymbolKind::Static => semantic_tokens::VARIABLE, SymbolKind::Struct => semantic_tokens::STRUCT, SymbolKind::Enum => semantic_tokens::ENUM, SymbolKind::Variant => semantic_tokens::ENUM_MEMBER, @@ -715,12 +716,14 @@ fn semantic_token_type_and_modifiers( }, }; + let mut mods = semantic_tokens::ModifierSet::default(); for modifier in highlight.mods.iter() { let modifier = match modifier { - HlMod::Associated => continue, + HlMod::Associated => semantic_tokens::ASSOCIATED, HlMod::Async => semantic_tokens::ASYNC, HlMod::Attribute => semantic_tokens::ATTRIBUTE_MODIFIER, HlMod::Callable => semantic_tokens::CALLABLE, + HlMod::Const => semantic_tokens::CONSTANT, HlMod::Consuming => semantic_tokens::CONSUMING, HlMod::ControlFlow => semantic_tokens::CONTROL_FLOW, HlMod::CrateRoot => semantic_tokens::CRATE_ROOT, @@ -742,7 +745,7 @@ fn semantic_token_type_and_modifiers( mods |= modifier; } - (type_, mods) + (ty, mods) } pub(crate) fn folding_range( @@ -814,9 +817,9 @@ pub(crate) fn url(snap: &GlobalStateSnapshot, file_id: FileId) -> lsp_types::Url /// When processing non-windows path, this is essentially the same as `Url::from_file_path`. pub(crate) fn url_from_abs_path(path: &AbsPath) -> lsp_types::Url { let url = lsp_types::Url::from_file_path(path).unwrap(); - match path.as_ref().components().next() { - Some(path::Component::Prefix(prefix)) - if matches!(prefix.kind(), path::Prefix::Disk(_) | path::Prefix::VerbatimDisk(_)) => + match path.components().next() { + Some(Utf8Component::Prefix(prefix)) + if matches!(prefix.kind(), Utf8Prefix::Disk(_) | Utf8Prefix::VerbatimDisk(_)) => { // Need to lowercase driver letter } @@ -1514,8 +1517,8 @@ pub(crate) fn test_item( snap: &GlobalStateSnapshot, test_item: ide::TestItem, line_index: Option<&LineIndex>, -) -> lsp_ext::TestItem { - lsp_ext::TestItem { +) -> Option { + Some(lsp_ext::TestItem { id: test_item.id, label: test_item.label, kind: match test_item.kind { @@ -1530,9 +1533,9 @@ pub(crate) fn test_item( | project_model::TargetKind::Example | project_model::TargetKind::BuildScript | project_model::TargetKind::Other => lsp_ext::TestItemKind::Package, - project_model::TargetKind::Test | project_model::TargetKind::Bench => { - lsp_ext::TestItemKind::Test - } + project_model::TargetKind::Test => lsp_ext::TestItemKind::Test, + // benches are not tests needed to be shown in the test explorer + project_model::TargetKind::Bench => return None, } } ide::TestItemKind::Module => lsp_ext::TestItemKind::Module, @@ -1548,7 +1551,7 @@ pub(crate) fn test_item( .map(|f| lsp_types::TextDocumentIdentifier { uri: url(snap, f) }), range: line_index.and_then(|l| Some(range(l, test_item.text_range?))), runnable: test_item.runnable.and_then(|r| runnable(snap, r).ok()), - } + }) } pub(crate) mod command { @@ -2728,12 +2731,12 @@ struct ProcMacro { #[test] #[cfg(target_os = "windows")] fn test_lowercase_drive_letter() { - use std::path::Path; + use paths::Utf8Path; - let url = url_from_abs_path(Path::new("C:\\Test").try_into().unwrap()); + let url = url_from_abs_path(Utf8Path::new("C:\\Test").try_into().unwrap()); assert_eq!(url.to_string(), "file:///c:/Test"); - let url = url_from_abs_path(Path::new(r#"\\localhost\C$\my_dir"#).try_into().unwrap()); + let url = url_from_abs_path(Utf8Path::new(r#"\\localhost\C$\my_dir"#).try_into().unwrap()); assert_eq!(url.to_string(), "file://localhost/C$/my_dir"); } } 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 ffe56e41435c7..38df32351256d 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 @@ -9,9 +9,8 @@ use std::{ use always_assert::always; use crossbeam_channel::{never, select, Receiver}; use ide_db::base_db::{SourceDatabase, SourceDatabaseExt, VfsPath}; -use itertools::Itertools; use lsp_server::{Connection, Notification, Request}; -use lsp_types::notification::Notification as _; +use lsp_types::{notification::Notification as _, TextDocumentIdentifier}; use stdx::thread::ThreadIntent; use vfs::FileId; @@ -533,31 +532,29 @@ impl GlobalState { let snapshot = self.snapshot(); move || { let tests = subscriptions - .into_iter() - .filter_map(|f| snapshot.analysis.crates_for(f).ok()) - .flatten() - .unique() - .filter_map(|c| snapshot.analysis.discover_tests_in_crate(c).ok()) + .iter() + .copied() + .filter_map(|f| snapshot.analysis.discover_tests_in_file(f).ok()) .flatten() .collect::>(); for t in &tests { hack_recover_crate_name::insert_name(t.id.clone()); } - let scope = tests - .iter() - .filter_map(|t| Some(t.id.split_once("::")?.0)) - .unique() - .map(|it| it.to_owned()) - .collect(); Task::DiscoverTest(lsp_ext::DiscoverTestResults { tests: tests .into_iter() - .map(|t| { + .filter_map(|t| { let line_index = t.file.and_then(|f| snapshot.file_line_index(f).ok()); to_proto::test_item(&snapshot, t, line_index.as_ref()) }) .collect(), - scope, + scope: None, + scope_file: Some( + subscriptions + .into_iter() + .map(|f| TextDocumentIdentifier { uri: to_proto::url(&snapshot, f) }) + .collect(), + ), }) } }); @@ -653,7 +650,7 @@ impl GlobalState { }; if let Some(state) = state { - self.report_progress("Building", state, msg, None, None); + self.report_progress("Building build-artifacts", state, msg, None, None); } } Task::LoadProcMacros(progress) => { @@ -669,7 +666,7 @@ impl GlobalState { }; if let Some(state) = state { - self.report_progress("Loading", state, msg, None, None); + self.report_progress("Loading proc-macros", state, msg, None, None); } } Task::BuildDepsHaveChanged => self.build_deps_changed = true, @@ -714,10 +711,9 @@ impl GlobalState { message += &format!( ": {}", match dir.strip_prefix(self.config.root_path()) { - Some(relative_path) => relative_path.as_ref(), + Some(relative_path) => relative_path.as_utf8_path(), None => dir.as_ref(), } - .display() ); } @@ -861,7 +857,7 @@ impl GlobalState { let title = if self.flycheck.len() == 1 { format!("{}", self.config.flycheck()) } else { - format!("cargo check (#{})", id + 1) + format!("{} (#{})", self.config.flycheck(), id + 1) }; self.report_progress( &title, 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 c2725e1fad919..771a5599f6f59 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -26,7 +26,6 @@ use itertools::Itertools; use load_cargo::{load_proc_macro, ProjectFolders}; use proc_macro_api::ProcMacroServer; use project_model::{ProjectWorkspace, WorkspaceBuildScripts}; -use rustc_hash::FxHashSet; use stdx::{format_to, thread::ThreadIntent}; use triomphe::Arc; use vfs::{AbsPath, AbsPathBuf, ChangeKind}; @@ -185,10 +184,8 @@ impl GlobalState { message.push_str( "`rust-analyzer.linkedProjects` have been specified, which may be incorrect. Specified project paths:\n", ); - message.push_str(&format!( - " {}", - self.config.linked_manifests().map(|it| it.display()).format("\n ") - )); + message + .push_str(&format!(" {}", self.config.linked_manifests().format("\n "))); if self.config.has_linked_project_jsons() { message.push_str("\nAdditionally, one or more project jsons are specified") } @@ -431,27 +428,46 @@ impl GlobalState { } if let FilesWatcher::Client = self.config.files().watcher { - let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions { - watchers: self - .workspaces - .iter() - .flat_map(|ws| ws.to_roots()) - .filter(|it| it.is_local) + let filter = + self.workspaces.iter().flat_map(|ws| ws.to_roots()).filter(|it| it.is_local); + + let watchers = if self.config.did_change_watched_files_relative_pattern_support() { + // When relative patterns are supported by the client, prefer using them + filter + .flat_map(|root| { + root.include.into_iter().flat_map(|base| { + [(base.clone(), "**/*.rs"), (base, "**/Cargo.{lock,toml}")] + }) + }) + .map(|(base, pat)| lsp_types::FileSystemWatcher { + glob_pattern: lsp_types::GlobPattern::Relative( + lsp_types::RelativePattern { + base_uri: lsp_types::OneOf::Right( + lsp_types::Url::from_file_path(base).unwrap(), + ), + pattern: pat.to_owned(), + }, + ), + kind: None, + }) + .collect() + } else { + // When they're not, integrate the base to make them into absolute patterns + filter .flat_map(|root| { - root.include.into_iter().flat_map(|it| { - [ - format!("{it}/**/*.rs"), - format!("{it}/**/Cargo.toml"), - format!("{it}/**/Cargo.lock"), - ] + root.include.into_iter().flat_map(|base| { + [format!("{base}/**/*.rs"), format!("{base}/**/Cargo.{{lock,toml}}")] }) }) .map(|glob_pattern| lsp_types::FileSystemWatcher { glob_pattern: lsp_types::GlobPattern::String(glob_pattern), kind: None, }) - .collect(), + .collect() }; + + let registration_options = + lsp_types::DidChangeWatchedFilesRegistrationOptions { watchers }; let registration = lsp_types::Registration { id: "workspace/didChangeWatchedFiles".to_owned(), method: "workspace/didChangeWatchedFiles".to_owned(), @@ -525,26 +541,23 @@ impl GlobalState { fn recreate_crate_graph(&mut self, cause: String) { // crate graph construction relies on these paths, record them so when one of them gets // deleted or created we trigger a reconstruction of the crate graph - let mut crate_graph_file_dependencies = FxHashSet::default(); + let mut crate_graph_file_dependencies = mem::take(&mut self.crate_graph_file_dependencies); + self.report_progress( + "Building CrateGraph", + crate::lsp::utils::Progress::Begin, + None, + None, + None, + ); let (crate_graph, proc_macro_paths, layouts, toolchains) = { // Create crate graph from all the workspaces let vfs = &mut self.vfs.write().0; - let loader = &mut self.loader; let load = |path: &AbsPath| { - let _p = tracing::span!(tracing::Level::DEBUG, "switch_workspaces::load").entered(); let vfs_path = vfs::VfsPath::from(path.to_path_buf()); crate_graph_file_dependencies.insert(vfs_path.clone()); - match vfs.file_id(&vfs_path) { - Some(file_id) => Some(file_id), - None => { - // FIXME: Consider not loading this here? - let contents = loader.handle.load_sync(path); - vfs.set_file_contents(vfs_path.clone(), contents); - vfs.file_id(&vfs_path) - } - } + vfs.file_id(&vfs_path) }; ws_to_crate_graph(&self.workspaces, self.config.extra_env(), load) @@ -564,6 +577,13 @@ impl GlobalState { change.set_toolchains(toolchains); self.analysis_host.apply_change(change); self.crate_graph_file_dependencies = crate_graph_file_dependencies; + self.report_progress( + "Building CrateGraph", + crate::lsp::utils::Progress::End, + None, + None, + None, + ); self.process_changes(); self.reload_flycheck(); @@ -732,6 +752,8 @@ pub fn ws_to_crate_graph( }); proc_macro_paths.push(crate_proc_macros); } + crate_graph.shrink_to_fit(); + proc_macro_paths.shrink_to_fit(); (crate_graph, proc_macro_paths, layouts, toolchains) } @@ -739,7 +761,7 @@ pub(crate) fn should_refresh_for_change(path: &AbsPath, change_kind: ChangeKind) const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"]; const IMPLICIT_TARGET_DIRS: &[&str] = &["src/bin", "examples", "tests", "benches"]; - let file_name = match path.file_name().unwrap_or_default().to_str() { + let file_name = match path.file_name() { Some(it) => it, None => return false, }; @@ -754,18 +776,18 @@ pub(crate) fn should_refresh_for_change(path: &AbsPath, change_kind: ChangeKind) // .cargo/config{.toml} if path.extension().unwrap_or_default() != "rs" { let is_cargo_config = matches!(file_name, "config.toml" | "config") - && path.parent().map(|parent| parent.as_ref().ends_with(".cargo")).unwrap_or(false); + && path.parent().map(|parent| parent.as_str().ends_with(".cargo")).unwrap_or(false); return is_cargo_config; } - if IMPLICIT_TARGET_FILES.iter().any(|it| path.as_ref().ends_with(it)) { + if IMPLICIT_TARGET_FILES.iter().any(|it| path.as_str().ends_with(it)) { return true; } let parent = match path.parent() { Some(it) => it, None => return false, }; - if IMPLICIT_TARGET_DIRS.iter().any(|it| parent.as_ref().ends_with(it)) { + if IMPLICIT_TARGET_DIRS.iter().any(|it| parent.as_str().ends_with(it)) { return true; } if file_name == "main.rs" { @@ -773,7 +795,7 @@ pub(crate) fn should_refresh_for_change(path: &AbsPath, change_kind: ChangeKind) Some(it) => it, None => return false, }; - if IMPLICIT_TARGET_DIRS.iter().any(|it| grand_parent.as_ref().ends_with(it)) { + if IMPLICIT_TARGET_DIRS.iter().any(|it| grand_parent.as_str().ends_with(it)) { return true; } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/crate_graph.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/crate_graph.rs index efd42fadf7e96..cf38032b94182 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/crate_graph.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/crate_graph.rs @@ -60,7 +60,7 @@ fn get_fake_sysroot() -> Sysroot { let sysroot_path = get_fake_sysroot_path(); // there's no `libexec/` directory with a `proc-macro-srv` binary in that // fake sysroot, so we give them both the same path: - let sysroot_dir = AbsPathBuf::assert(sysroot_path); + let sysroot_dir = AbsPathBuf::assert_utf8(sysroot_path); let sysroot_src_dir = sysroot_dir.clone(); Sysroot::load(sysroot_dir, Some(Ok(sysroot_src_dir)), false) } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs index 960f5b531d44f..439b006977d3e 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 @@ -903,6 +903,7 @@ fn out_dirs_check() { } #[test] +#[cfg(not(windows))] // windows requires elevated permissions to create symlinks fn root_contains_symlink_out_dirs_check() { out_dirs_check_impl(true); } @@ -917,7 +918,7 @@ fn resolve_proc_macro() { } let sysroot = project_model::Sysroot::discover_no_source( - &AbsPathBuf::assert(std::env::current_dir().unwrap()), + &AbsPathBuf::assert_utf8(std::env::current_dir().unwrap()), &Default::default(), ) .unwrap(); @@ -1002,7 +1003,7 @@ pub fn foo(_input: TokenStream) -> TokenStream { }, "procMacro": { "enable": true, - "server": proc_macro_server_path.as_path().as_ref(), + "server": proc_macro_server_path.as_path().as_str(), } })) .root("foo") @@ -1039,7 +1040,7 @@ fn test_will_rename_files_same_level() { let tmp_dir = TestDir::new(); let tmp_dir_path = tmp_dir.path().to_owned(); - let tmp_dir_str = tmp_dir_path.to_str().unwrap(); + let tmp_dir_str = tmp_dir_path.as_str(); let base_path = PathBuf::from(format!("file://{tmp_dir_str}")); let code = r#" @@ -1084,7 +1085,7 @@ use crate::old_folder::nested::foo as bar; "documentChanges": [ { "textDocument": { - "uri": format!("file://{}", tmp_dir_path.join("src").join("lib.rs").to_str().unwrap().to_owned().replace("C:\\", "/c:/").replace('\\', "/")), + "uri": format!("file://{}", tmp_dir_path.join("src").join("lib.rs").as_str().to_owned().replace("C:\\", "/c:/").replace('\\', "/")), "version": null }, "edits": [ @@ -1141,7 +1142,7 @@ use crate::old_folder::nested::foo as bar; "documentChanges": [ { "textDocument": { - "uri": format!("file://{}", tmp_dir_path.join("src").join("lib.rs").to_str().unwrap().to_owned().replace("C:\\", "/c:/").replace('\\', "/")), + "uri": format!("file://{}", tmp_dir_path.join("src").join("lib.rs").as_str().to_owned().replace("C:\\", "/c:/").replace('\\', "/")), "version": null }, "edits": [ @@ -1162,7 +1163,7 @@ use crate::old_folder::nested::foo as bar; }, { "textDocument": { - "uri": format!("file://{}", tmp_dir_path.join("src").join("old_folder").join("nested.rs").to_str().unwrap().to_owned().replace("C:\\", "/c:/").replace('\\', "/")), + "uri": format!("file://{}", tmp_dir_path.join("src").join("old_folder").join("nested.rs").as_str().to_owned().replace("C:\\", "/c:/").replace('\\', "/")), "version": null }, "edits": [ diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs index 1d831b8b10532..8bbe6ff37247b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs @@ -1,7 +1,6 @@ use std::{ cell::{Cell, RefCell}, fs, - path::{Path, PathBuf}, sync::Once, time::Duration, }; @@ -9,6 +8,7 @@ use std::{ use crossbeam_channel::{after, select, Receiver}; use lsp_server::{Connection, Message, Notification, Request}; use lsp_types::{notification::Exit, request::Shutdown, TextDocumentIdentifier, Url}; +use paths::{Utf8Path, Utf8PathBuf}; use rust_analyzer::{config::Config, lsp, main_loop}; use serde::Serialize; use serde_json::{json, to_string_pretty, Value}; @@ -21,7 +21,7 @@ use crate::testdir::TestDir; pub(crate) struct Project<'a> { fixture: &'a str, tmp_dir: Option, - roots: Vec, + roots: Vec, config: serde_json::Value, root_dir_contains_symlink: bool, } @@ -359,7 +359,7 @@ impl Server { self.client.sender.send(Message::Notification(not)).unwrap(); } - pub(crate) fn path(&self) -> &Path { + pub(crate) fn path(&self) -> &Utf8Path { self.dir.path() } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/testdir.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/testdir.rs index b3ee7fa3d03fe..d113bd512789a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/testdir.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/testdir.rs @@ -1,11 +1,12 @@ use std::{ fs, io, - path::{Path, PathBuf}, sync::atomic::{AtomicUsize, Ordering}, }; +use paths::{Utf8Path, Utf8PathBuf}; + pub(crate) struct TestDir { - path: PathBuf, + path: Utf8PathBuf, keep: bool, } @@ -51,10 +52,13 @@ impl TestDir { #[cfg(target_os = "windows")] std::os::windows::fs::symlink_dir(path, &symlink_path).unwrap(); - return TestDir { path: symlink_path, keep: false }; + return TestDir { + path: Utf8PathBuf::from_path_buf(symlink_path).unwrap(), + keep: false, + }; } - return TestDir { path, keep: false }; + return TestDir { path: Utf8PathBuf::from_path_buf(path).unwrap(), keep: false }; } panic!("Failed to create a temporary directory") } @@ -64,7 +68,7 @@ impl TestDir { self.keep = true; self } - pub(crate) fn path(&self) -> &Path { + pub(crate) fn path(&self) -> &Utf8Path { &self.path } } @@ -79,7 +83,7 @@ impl Drop for TestDir { let actual_path = filetype.is_symlink().then(|| fs::read_link(&self.path).unwrap()); if let Some(actual_path) = actual_path { - remove_dir_all(&actual_path).unwrap_or_else(|err| { + remove_dir_all(Utf8Path::from_path(&actual_path).unwrap()).unwrap_or_else(|err| { panic!( "failed to remove temporary link to directory {}: {err}", actual_path.display() @@ -88,18 +92,18 @@ impl Drop for TestDir { } remove_dir_all(&self.path).unwrap_or_else(|err| { - panic!("failed to remove temporary directory {}: {err}", self.path.display()) + panic!("failed to remove temporary directory {}: {err}", self.path) }); } } #[cfg(not(windows))] -fn remove_dir_all(path: &Path) -> io::Result<()> { +fn remove_dir_all(path: &Utf8Path) -> io::Result<()> { fs::remove_dir_all(path) } #[cfg(windows)] -fn remove_dir_all(path: &Path) -> io::Result<()> { +fn remove_dir_all(path: &Utf8Path) -> io::Result<()> { for _ in 0..99 { if fs::remove_dir_all(path).is_ok() { return Ok(()); diff --git a/src/tools/rust-analyzer/crates/span/src/lib.rs b/src/tools/rust-analyzer/crates/span/src/lib.rs index 6b849ce373812..c9109c72d0d2e 100644 --- a/src/tools/rust-analyzer/crates/span/src/lib.rs +++ b/src/tools/rust-analyzer/crates/span/src/lib.rs @@ -16,6 +16,56 @@ pub use self::{ pub use syntax::{TextRange, TextSize}; pub use vfs::FileId; +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Edition { + Edition2015, + Edition2018, + Edition2021, + Edition2024, +} + +impl Edition { + pub const CURRENT: Edition = Edition::Edition2021; + pub const DEFAULT: Edition = Edition::Edition2015; +} + +#[derive(Debug)] +pub struct ParseEditionError { + invalid_input: String, +} + +impl std::error::Error for ParseEditionError {} +impl fmt::Display for ParseEditionError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "invalid edition: {:?}", self.invalid_input) + } +} + +impl std::str::FromStr for Edition { + type Err = ParseEditionError; + + fn from_str(s: &str) -> Result { + let res = match s { + "2015" => Edition::Edition2015, + "2018" => Edition::Edition2018, + "2021" => Edition::Edition2021, + "2024" => Edition::Edition2024, + _ => return Err(ParseEditionError { invalid_input: s.to_owned() }), + }; + Ok(res) + } +} + +impl fmt::Display for Edition { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + Edition::Edition2015 => "2015", + Edition::Edition2018 => "2018", + Edition::Edition2021 => "2021", + Edition::Edition2024 => "2024", + }) + } +} #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct FilePosition { pub file_id: FileId, diff --git a/src/tools/rust-analyzer/crates/syntax/Cargo.toml b/src/tools/rust-analyzer/crates/syntax/Cargo.toml index 9a8d73cf7ff47..1809ca7dea5d6 100644 --- a/src/tools/rust-analyzer/crates/syntax/Cargo.toml +++ b/src/tools/rust-analyzer/crates/syntax/Cargo.toml @@ -33,12 +33,8 @@ text-edit.workspace = true [dev-dependencies] rayon.workspace = true expect-test = "1.4.0" -proc-macro2 = "1.0.47" -quote = "1.0.20" -ungrammar = "1.16.1" test-utils.workspace = true -sourcegen.workspace = true [features] in-rust-tree = [] diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index c3d8e97c436cc..e1765b25fd822 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -414,7 +414,7 @@ StmtList = '}' RefExpr = - Attr* '&' ('raw' | 'mut' | 'const') Expr + Attr* '&' (('raw' 'const'?)| ('raw'? 'mut') ) Expr TryExpr = Attr* Expr '?' 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 75971861aa80e..c82bc4151ac0f 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 @@ -8,110 +8,71 @@ use crate::{ }; #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Name { - pub(crate) syntax: SyntaxNode, -} -impl Name { - pub fn ident_token(&self) -> Option { support::token(&self.syntax, T![ident]) } - pub fn self_token(&self) -> Option { support::token(&self.syntax, T![self]) } -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct NameRef { +pub struct Abi { pub(crate) syntax: SyntaxNode, } -impl NameRef { - pub fn ident_token(&self) -> Option { support::token(&self.syntax, T![ident]) } - pub fn self_token(&self) -> Option { support::token(&self.syntax, T![self]) } - pub fn super_token(&self) -> Option { support::token(&self.syntax, T![super]) } - pub fn crate_token(&self) -> Option { support::token(&self.syntax, T![crate]) } - pub fn Self_token(&self) -> Option { support::token(&self.syntax, T![Self]) } +impl Abi { + pub fn extern_token(&self) -> Option { support::token(&self.syntax, T![extern]) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Lifetime { +pub struct ArgList { pub(crate) syntax: SyntaxNode, } -impl Lifetime { - pub fn lifetime_ident_token(&self) -> Option { - support::token(&self.syntax, T![lifetime_ident]) - } +impl ArgList { + pub fn args(&self) -> AstChildren { support::children(&self.syntax) } + pub fn l_paren_token(&self) -> Option { support::token(&self.syntax, T!['(']) } + pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Path { +pub struct ArrayExpr { pub(crate) syntax: SyntaxNode, } -impl Path { - pub fn qualifier(&self) -> Option { support::child(&self.syntax) } - pub fn coloncolon_token(&self) -> Option { support::token(&self.syntax, T![::]) } - pub fn segment(&self) -> Option { support::child(&self.syntax) } +impl ast::HasAttrs for ArrayExpr {} +impl ArrayExpr { + pub fn expr(&self) -> Option { support::child(&self.syntax) } + pub fn exprs(&self) -> AstChildren { support::children(&self.syntax) } + pub fn l_brack_token(&self) -> Option { support::token(&self.syntax, T!['[']) } + pub fn r_brack_token(&self) -> Option { support::token(&self.syntax, T![']']) } + pub fn semicolon_token(&self) -> Option { support::token(&self.syntax, T![;]) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct PathSegment { +pub struct ArrayType { pub(crate) syntax: SyntaxNode, } -impl PathSegment { - pub fn coloncolon_token(&self) -> Option { support::token(&self.syntax, T![::]) } - pub fn name_ref(&self) -> Option { support::child(&self.syntax) } - pub fn generic_arg_list(&self) -> Option { support::child(&self.syntax) } - pub fn param_list(&self) -> Option { support::child(&self.syntax) } - pub fn ret_type(&self) -> Option { support::child(&self.syntax) } - pub fn l_angle_token(&self) -> Option { support::token(&self.syntax, T![<]) } +impl ArrayType { + pub fn const_arg(&self) -> Option { support::child(&self.syntax) } pub fn ty(&self) -> Option { support::child(&self.syntax) } - pub fn as_token(&self) -> Option { support::token(&self.syntax, T![as]) } - pub fn path_type(&self) -> Option { support::child(&self.syntax) } - pub fn r_angle_token(&self) -> Option { support::token(&self.syntax, T![>]) } -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct GenericArgList { - pub(crate) syntax: SyntaxNode, -} -impl GenericArgList { - pub fn coloncolon_token(&self) -> Option { support::token(&self.syntax, T![::]) } - pub fn l_angle_token(&self) -> Option { support::token(&self.syntax, T![<]) } - pub fn generic_args(&self) -> AstChildren { support::children(&self.syntax) } - pub fn r_angle_token(&self) -> Option { support::token(&self.syntax, T![>]) } + pub fn l_brack_token(&self) -> Option { support::token(&self.syntax, T!['[']) } + pub fn r_brack_token(&self) -> Option { support::token(&self.syntax, T![']']) } + pub fn semicolon_token(&self) -> Option { support::token(&self.syntax, T![;]) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct ParamList { +pub struct AsmExpr { pub(crate) syntax: SyntaxNode, } -impl ParamList { +impl ast::HasAttrs for AsmExpr {} +impl AsmExpr { + pub fn expr(&self) -> Option { support::child(&self.syntax) } + pub fn pound_token(&self) -> Option { support::token(&self.syntax, T![#]) } pub fn l_paren_token(&self) -> Option { support::token(&self.syntax, T!['(']) } - pub fn self_param(&self) -> Option { support::child(&self.syntax) } - pub fn comma_token(&self) -> Option { support::token(&self.syntax, T![,]) } - pub fn params(&self) -> AstChildren { support::children(&self.syntax) } pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) } - pub fn pipe_token(&self) -> Option { support::token(&self.syntax, T![|]) } -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct RetType { - pub(crate) syntax: SyntaxNode, -} -impl RetType { - pub fn thin_arrow_token(&self) -> Option { support::token(&self.syntax, T![->]) } - pub fn ty(&self) -> Option { support::child(&self.syntax) } -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct PathType { - pub(crate) syntax: SyntaxNode, -} -impl PathType { - pub fn path(&self) -> Option { support::child(&self.syntax) } + pub fn asm_token(&self) -> Option { support::token(&self.syntax, T![asm]) } + pub fn builtin_token(&self) -> Option { support::token(&self.syntax, T![builtin]) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct TypeArg { +pub struct AssocItemList { pub(crate) syntax: SyntaxNode, } -impl TypeArg { - pub fn ty(&self) -> Option { support::child(&self.syntax) } +impl ast::HasAttrs for AssocItemList {} +impl AssocItemList { + pub fn assoc_items(&self) -> AstChildren { support::children(&self.syntax) } + pub fn l_curly_token(&self) -> Option { support::token(&self.syntax, T!['{']) } + pub fn r_curly_token(&self) -> Option { support::token(&self.syntax, T!['}']) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -120,116 +81,125 @@ pub struct AssocTypeArg { } impl ast::HasTypeBounds for AssocTypeArg {} impl AssocTypeArg { - pub fn name_ref(&self) -> Option { support::child(&self.syntax) } + pub fn const_arg(&self) -> Option { support::child(&self.syntax) } pub fn generic_arg_list(&self) -> Option { support::child(&self.syntax) } + pub fn name_ref(&self) -> Option { support::child(&self.syntax) } pub fn param_list(&self) -> Option { support::child(&self.syntax) } pub fn ret_type(&self) -> Option { support::child(&self.syntax) } - pub fn eq_token(&self) -> Option { support::token(&self.syntax, T![=]) } pub fn ty(&self) -> Option { support::child(&self.syntax) } - pub fn const_arg(&self) -> Option { support::child(&self.syntax) } + pub fn eq_token(&self) -> Option { support::token(&self.syntax, T![=]) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct LifetimeArg { +pub struct Attr { pub(crate) syntax: SyntaxNode, } -impl LifetimeArg { - pub fn lifetime(&self) -> Option { support::child(&self.syntax) } +impl Attr { + pub fn meta(&self) -> Option { support::child(&self.syntax) } + pub fn excl_token(&self) -> Option { support::token(&self.syntax, T![!]) } + pub fn pound_token(&self) -> Option { support::token(&self.syntax, T![#]) } + pub fn l_brack_token(&self) -> Option { support::token(&self.syntax, T!['[']) } + pub fn r_brack_token(&self) -> Option { support::token(&self.syntax, T![']']) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct ConstArg { +pub struct AwaitExpr { pub(crate) syntax: SyntaxNode, } -impl ConstArg { +impl ast::HasAttrs for AwaitExpr {} +impl AwaitExpr { pub fn expr(&self) -> Option { support::child(&self.syntax) } + pub fn dot_token(&self) -> Option { support::token(&self.syntax, T![.]) } + pub fn await_token(&self) -> Option { support::token(&self.syntax, T![await]) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct TypeBoundList { +pub struct BecomeExpr { pub(crate) syntax: SyntaxNode, } -impl TypeBoundList { - pub fn bounds(&self) -> AstChildren { support::children(&self.syntax) } +impl ast::HasAttrs for BecomeExpr {} +impl BecomeExpr { + pub fn expr(&self) -> Option { support::child(&self.syntax) } + pub fn become_token(&self) -> Option { support::token(&self.syntax, T![become]) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct MacroCall { +pub struct BinExpr { pub(crate) syntax: SyntaxNode, } -impl ast::HasAttrs for MacroCall {} -impl ast::HasDocComments for MacroCall {} -impl MacroCall { - pub fn path(&self) -> Option { support::child(&self.syntax) } - pub fn excl_token(&self) -> Option { support::token(&self.syntax, T![!]) } - pub fn token_tree(&self) -> Option { support::child(&self.syntax) } - pub fn semicolon_token(&self) -> Option { support::token(&self.syntax, T![;]) } -} +impl ast::HasAttrs for BinExpr {} +impl BinExpr {} #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Attr { +pub struct BlockExpr { pub(crate) syntax: SyntaxNode, } -impl Attr { - pub fn pound_token(&self) -> Option { support::token(&self.syntax, T![#]) } - pub fn excl_token(&self) -> Option { support::token(&self.syntax, T![!]) } - pub fn l_brack_token(&self) -> Option { support::token(&self.syntax, T!['[']) } - pub fn meta(&self) -> Option { support::child(&self.syntax) } - pub fn r_brack_token(&self) -> Option { support::token(&self.syntax, T![']']) } +impl ast::HasAttrs for BlockExpr {} +impl BlockExpr { + pub fn label(&self) -> Option` container. @@ -190,60 +190,60 @@ store-value: (hover_background_color, "#616161") // hover background color store-value: (grey, "#ccc") call-function: ( - "check-result-color", ( - "keyword", // item kind - "#d2991d", // color of item kind - "#d2991d", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "keyword", + "color": "#d2991d", + "hover_color": "#d2991d", + }, ) call-function: ( - "check-result-color", ( - "struct", // item kind - "#2dbfb8", // color of item kind - "#2dbfb8", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "struct", + "color": "#2dbfb8", + "hover_color": "#2dbfb8", + }, ) call-function: ( - "check-result-color", ( - "associatedtype", // item kind - "#d2991d", // color of item kind - "#d2991d", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "associatedtype", + "color": "#d2991d", + "hover_color": "#d2991d", + }, ) call-function: ( - "check-result-color", ( - "tymethod", // item kind - "#2bab63", // color of item kind - "#2bab63", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "tymethod", + "color": "#2bab63", + "hover_color": "#2bab63", + }, ) call-function: ( - "check-result-color", ( - "method", // item kind - "#2bab63", // color of item kind - "#2bab63", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "method", + "color": "#2bab63", + "hover_color": "#2bab63", + }, ) call-function: ( - "check-result-color", ( - "structfield", // item kind - "#ddd", // color of item kind - "#ddd", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "structfield", + "color": "#ddd", + "hover_color": "#ddd", + }, ) call-function: ( - "check-result-color", ( - "macro", // item kind - "#09bd00", // color of item kind - "#09bd00", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "macro", + "color": "#09bd00", + "hover_color": "#09bd00", + }, ) call-function: ( - "check-result-color", ( - "fn", // item kind - "#2bab63", // color of item kind - "#2bab63", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "fn", + "color": "#2bab63", + "hover_color": "#2bab63", + }, ) // Checking the `` container. @@ -287,60 +287,60 @@ store-value: (hover_background_color, "#ccc") // hover background color store-value: (grey, "#999") call-function: ( - "check-result-color", ( - "keyword", // item kind - "#3873ad", // color of item kind - "#3873ad", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "keyword", + "color": "#3873ad", + "hover_color": "#3873ad", + }, ) call-function: ( - "check-result-color", ( - "struct", // item kind - "#ad378a", // color of item kind - "#ad378a", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "struct", + "color": "#ad378a", + "hover_color": "#ad378a", + }, ) call-function: ( - "check-result-color", ( - "associatedtype", // item kind - "#3873ad", // color of item kind - "#3873ad", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "associatedtype", + "color": "#3873ad", + "hover_color": "#3873ad", + }, ) call-function: ( - "check-result-color", ( - "tymethod", // item kind - "#ad7c37", // color of item kind - "#ad7c37", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "tymethod", + "color": "#ad7c37", + "hover_color": "#ad7c37", + }, ) call-function: ( - "check-result-color", ( - "method", // item kind - "#ad7c37", // color of item kind - "#ad7c37", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "method", + "color": "#ad7c37", + "hover_color": "#ad7c37", + }, ) call-function: ( - "check-result-color", ( - "structfield", // item kind - "#000", // color of item kind - "#000", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "structfield", + "color": "#000", + "hover_color": "#000", + }, ) call-function: ( - "check-result-color", ( - "macro", // item kind - "#068000", // color of item kind - "#068000", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "macro", + "color": "#068000", + "hover_color": "#068000", + }, ) call-function: ( - "check-result-color", ( - "fn", // item kind - "#ad7c37", // color of item kind - "#ad7c37", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "fn", + "color": "#ad7c37", + "hover_color": "#ad7c37", + }, ) // Checking the `` container. @@ -358,11 +358,11 @@ show-text: true define-function: ( "check-alias", - (theme, alias, grey), + [theme, alias, grey], block { set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"} reload: - write: (".search-input", "thisisanalias") + write-into: (".search-input", "thisisanalias") // To be SURE that the search will be run. press-key: 'Enter' // Waiting for the search results to appear... diff --git a/tests/rustdoc-gui/search-result-display.goml b/tests/rustdoc-gui/search-result-display.goml index 6ce13b8c3d30f..b1a5548808e01 100644 --- a/tests/rustdoc-gui/search-result-display.goml +++ b/tests/rustdoc-gui/search-result-display.goml @@ -2,7 +2,7 @@ // Checks that the search results have the expected width. go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" set-window-size: (900, 1000) -write: (".search-input", "test") +write-into: (".search-input", "test") // To be SURE that the search will be run. press-key: 'Enter' wait-for: "#crate-search" @@ -69,7 +69,7 @@ assert-css: ("#search", {"width": "640px"}) show-text: true define-function: ( "check-filter", - (theme, border, filter, hover_border, hover_filter), + [theme, border, filter, hover_border, hover_filter], block { set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"} reload: diff --git a/tests/rustdoc-gui/search-result-impl-disambiguation.goml b/tests/rustdoc-gui/search-result-impl-disambiguation.goml index 6d12032e891b6..3e49ac33025ee 100644 --- a/tests/rustdoc-gui/search-result-impl-disambiguation.goml +++ b/tests/rustdoc-gui/search-result-impl-disambiguation.goml @@ -5,7 +5,7 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" // This should link to the inherent impl -write: (".search-input", "ZyxwvutMethodDisambiguation -> bool") +write-into: (".search-input", "ZyxwvutMethodDisambiguation -> bool") // To be SURE that the search will be run. press-key: 'Enter' // Waiting for the search results to appear... @@ -25,7 +25,7 @@ assert: "section:target" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" // This should link to the trait impl -write: (".search-input", "ZyxwvutMethodDisambiguation, usize -> usize") +write-into: (".search-input", "ZyxwvutMethodDisambiguation, usize -> usize") // To be SURE that the search will be run. press-key: 'Enter' // Waiting for the search results to appear... diff --git a/tests/rustdoc-gui/search-result-keyword.goml b/tests/rustdoc-gui/search-result-keyword.goml index 1b2be6d4e3e22..370edce2ddd04 100644 --- a/tests/rustdoc-gui/search-result-keyword.goml +++ b/tests/rustdoc-gui/search-result-keyword.goml @@ -1,6 +1,6 @@ // Checks that the "keyword" results have the expected text alongside them. go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" -write: (".search-input", "CookieMonster") +write-into: (".search-input", "CookieMonster") // To be SURE that the search will be run. press-key: 'Enter' // Waiting for the search results to appear... diff --git a/tests/rustdoc-gui/search-tab-change-title-fn-sig.goml b/tests/rustdoc-gui/search-tab-change-title-fn-sig.goml index 156d8d03ca2d1..7e26229ec6e51 100644 --- a/tests/rustdoc-gui/search-tab-change-title-fn-sig.goml +++ b/tests/rustdoc-gui/search-tab-change-title-fn-sig.goml @@ -1,7 +1,7 @@ // Checks that the search tab results work correctly with function signature syntax // First, try a search-by-name go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" -write: (".search-input", "Foo") +write-into: (".search-input", "Foo") // To be SURE that the search will be run. press-key: 'Enter' // Waiting for the search results to appear... @@ -23,7 +23,7 @@ wait-for-attribute: ("#search-tabs > button:nth-of-type(3)", {"class": "selected // Now try search-by-return go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" -write: (".search-input", "-> String") +write-into: (".search-input", "-> String") // To be SURE that the search will be run. press-key: 'Enter' // Waiting for the search results to appear... @@ -45,7 +45,7 @@ wait-for-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected // Try with a search-by-return with no results go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" -write: (".search-input", "-> Something") +write-into: (".search-input", "-> Something") // To be SURE that the search will be run. press-key: 'Enter' // Waiting for the search results to appear... @@ -55,7 +55,7 @@ assert-text: ("#search-tabs > button:nth-of-type(1)", "In Function Return Types" // Try with a search-by-parameter go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" -write: (".search-input", "usize,pattern") +write-into: (".search-input", "usize,pattern") // To be SURE that the search will be run. press-key: 'Enter' // Waiting for the search results to appear... @@ -65,7 +65,7 @@ assert-text: ("#search-tabs > button:nth-of-type(1)", "In Function Parameters", // Try with a search-by-parameter-and-return go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" -write: (".search-input", "pattern -> str") +write-into: (".search-input", "pattern -> str") // To be SURE that the search will be run. press-key: 'Enter' // Waiting for the search results to appear... diff --git a/tests/rustdoc-gui/search-tab.goml b/tests/rustdoc-gui/search-tab.goml index b52bb0688c1b5..c33866593c355 100644 --- a/tests/rustdoc-gui/search-tab.goml +++ b/tests/rustdoc-gui/search-tab.goml @@ -4,9 +4,9 @@ show-text: true define-function: ( "check-colors", - (theme, background, background_selected, background_hover, border_bottom, + [theme, background, background_selected, background_hover, border_bottom, border_bottom_selected, border_bottom_hover, border_top, border_top_selected, - border_top_hover), + border_top_hover], block { // Setting the theme. set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"} @@ -93,12 +93,12 @@ assert-property: ("#search-tabs > button:nth-child(3) > .count", {"offsetWidth": compare-elements-position: ( "#search-tabs > button:nth-child(1) > .count", "#search-tabs > button:nth-child(2) > .count", - ("y") + ["y"] ) compare-elements-position: ( "#search-tabs > button:nth-child(2) > .count", "#search-tabs > button:nth-child(3) > .count", - ("y") + ["y"] ) // Check that counts are beside the titles and haven't wrapped compare-elements-position-near: ( @@ -135,12 +135,12 @@ assert-property: ("#search-tabs > button:nth-child(3) > .count", {"offsetWidth": compare-elements-position: ( "#search-tabs > button:nth-child(1) > .count", "#search-tabs > button:nth-child(2) > .count", - ("y") + ["y"] ) compare-elements-position: ( "#search-tabs > button:nth-child(2) > .count", "#search-tabs > button:nth-child(3) > .count", - ("y") + ["y"] ) // Check that counts are NOT beside the titles; now they have wrapped compare-elements-position-near-false: ( diff --git a/tests/rustdoc-gui/setting-auto-hide-content-large-items.goml b/tests/rustdoc-gui/setting-auto-hide-content-large-items.goml index b55a1cfd92bc5..9afde7c61da69 100644 --- a/tests/rustdoc-gui/setting-auto-hide-content-large-items.goml +++ b/tests/rustdoc-gui/setting-auto-hide-content-large-items.goml @@ -6,7 +6,7 @@ fail-on-request-error: false define-function: ( "check-setting", - (storage_value, setting_attribute_value, toggle_attribute_value), + [storage_value, setting_attribute_value, toggle_attribute_value], block { assert-local-storage: {"rustdoc-auto-hide-large-items": |storage_value|} click: "#settings-menu" diff --git a/tests/rustdoc-gui/setting-auto-hide-item-methods-docs.goml b/tests/rustdoc-gui/setting-auto-hide-item-methods-docs.goml index 5210ad8f793b1..644396ed57826 100644 --- a/tests/rustdoc-gui/setting-auto-hide-item-methods-docs.goml +++ b/tests/rustdoc-gui/setting-auto-hide-item-methods-docs.goml @@ -3,7 +3,7 @@ define-function: ( "check-setting", - (storage_value, setting_attribute_value, toggle_attribute_value), + [storage_value, setting_attribute_value, toggle_attribute_value], block { assert-local-storage: {"rustdoc-auto-hide-method-docs": |storage_value|} click: "#settings-menu" diff --git a/tests/rustdoc-gui/setting-auto-hide-trait-implementations.goml b/tests/rustdoc-gui/setting-auto-hide-trait-implementations.goml index ecadd8fa80ede..3c09198dae509 100644 --- a/tests/rustdoc-gui/setting-auto-hide-trait-implementations.goml +++ b/tests/rustdoc-gui/setting-auto-hide-trait-implementations.goml @@ -2,7 +2,7 @@ define-function: ( "check-setting", - (storage_value, setting_attribute_value, toggle_attribute_value), + [storage_value, setting_attribute_value, toggle_attribute_value], block { assert-local-storage: {"rustdoc-auto-hide-trait-implementations": |storage_value|} click: "#settings-menu" diff --git a/tests/rustdoc-gui/setting-go-to-only-result.goml b/tests/rustdoc-gui/setting-go-to-only-result.goml index 45e0b349051ee..f8535477c22d0 100644 --- a/tests/rustdoc-gui/setting-go-to-only-result.goml +++ b/tests/rustdoc-gui/setting-go-to-only-result.goml @@ -2,7 +2,7 @@ define-function: ( "check-setting", - (storage_value, setting_attribute_value), + [storage_value, setting_attribute_value], block { assert-local-storage: {"rustdoc-go-to-only-result": |storage_value|} click: "#settings-menu" @@ -32,7 +32,7 @@ assert-local-storage: {"rustdoc-go-to-only-result": "true"} go-to: "file://" + |DOC_PATH| + "/lib2/index.html" // We enter it into the search. -write: (".search-input", "HasALongTraitWithParams") +write-into: (".search-input", "HasALongTraitWithParams") wait-for-document-property: {"title": "HasALongTraitWithParams in lib2 - Rust"} assert-window-property: ({"location": "/lib2/struct.HasALongTraitWithParams.html"}, ENDS_WITH) diff --git a/tests/rustdoc-gui/settings.goml b/tests/rustdoc-gui/settings.goml index e40c637dcf7dd..0bb21c28cb529 100644 --- a/tests/rustdoc-gui/settings.goml +++ b/tests/rustdoc-gui/settings.goml @@ -304,7 +304,7 @@ wait-for: "#settings" assert-css: (".setting-radio", {"cursor": "pointer"}) assert-attribute-false: ("#settings", {"class": "popover"}, CONTAINS) -compare-elements-position: (".sub form", "#settings", ("x")) +compare-elements-position: (".sub form", "#settings", ["x"]) // Check that setting-line has the same margin in this mode as in the popover. assert-css: (".setting-line", {"margin": |setting_line_margin|}) diff --git a/tests/rustdoc-gui/sidebar-links-color.goml b/tests/rustdoc-gui/sidebar-links-color.goml index 774fbcac1e2c4..0edffc51a816d 100644 --- a/tests/rustdoc-gui/sidebar-links-color.goml +++ b/tests/rustdoc-gui/sidebar-links-color.goml @@ -6,12 +6,12 @@ show-text: true define-function: ( "check-colors", - ( + [ theme, struct, struct_hover, struct_hover_background, enum, enum_hover, enum_hover_background, union, union_hover, union_hover_background, trait, trait_hover, trait_hover_background, fn, fn_hover, fn_hover_background, type, type_hover, type_hover_background, keyword, keyword_hover, keyword_hover_background, - ), + ], block { set-local-storage: { "rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false" } reload: diff --git a/tests/rustdoc-gui/sidebar-mobile.goml b/tests/rustdoc-gui/sidebar-mobile.goml index d3a82d9ebe6fc..8843de8d7e943 100644 --- a/tests/rustdoc-gui/sidebar-mobile.goml +++ b/tests/rustdoc-gui/sidebar-mobile.goml @@ -57,7 +57,7 @@ show-text: true define-function: ( "check-colors", - (theme, color, background), + [theme, color, background], block { set-local-storage: {"rustdoc-use-system-theme": "false", "rustdoc-theme": |theme|} reload: diff --git a/tests/rustdoc-gui/sidebar-source-code-display.goml b/tests/rustdoc-gui/sidebar-source-code-display.goml index 5149d4991f731..41c8e45f4a6d3 100644 --- a/tests/rustdoc-gui/sidebar-source-code-display.goml +++ b/tests/rustdoc-gui/sidebar-source-code-display.goml @@ -30,9 +30,9 @@ show-text: true define-function: ( "check-colors", - ( + [ theme, color, color_hover, background, background_hover, background_toggle, - ), + ], block { set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"} reload: diff --git a/tests/rustdoc-gui/sidebar-source-code.goml b/tests/rustdoc-gui/sidebar-source-code.goml index d7de43a2243ab..3f7ef643d18e7 100644 --- a/tests/rustdoc-gui/sidebar-source-code.goml +++ b/tests/rustdoc-gui/sidebar-source-code.goml @@ -6,7 +6,7 @@ show-text: true // First, check the sidebar colors. define-function: ( "check-colors", - (theme, color, background_color), + [theme, color, background_color], block { set-local-storage: { "rustdoc-theme": |theme|, diff --git a/tests/rustdoc-gui/sidebar.goml b/tests/rustdoc-gui/sidebar.goml index 82b4f2e9429c6..115b1eb323c4d 100644 --- a/tests/rustdoc-gui/sidebar.goml +++ b/tests/rustdoc-gui/sidebar.goml @@ -6,7 +6,7 @@ show-text: true // First, check the sidebar colors. define-function: ( "check-colors", - (theme, color, background_color), + [theme, color, background_color], block { set-local-storage: { "rustdoc-theme": |theme|, diff --git a/tests/rustdoc-gui/source-code-page.goml b/tests/rustdoc-gui/source-code-page.goml index 8b4d7617e0cdb..e29d123d2270b 100644 --- a/tests/rustdoc-gui/source-code-page.goml +++ b/tests/rustdoc-gui/source-code-page.goml @@ -21,7 +21,7 @@ assert-attribute-false: (".src-line-numbers > a:nth-child(7)", {"class": "line-h define-function: ( "check-colors", - (theme, color, background_color, highlight_color, highlight_background_color), + [theme, color, background_color, highlight_color, highlight_background_color], block { set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"} reload: @@ -61,7 +61,7 @@ call-function: ("check-colors", { }) // This is to ensure that the content is correctly align with the line numbers. -compare-elements-position: ("//*[@id='1']", ".rust > code > span", ("y")) +compare-elements-position: ("//*[@id='1']", ".rust > code > span", ["y"]) // Check the `href` property so that users can treat anchors as links. assert-property: (".src-line-numbers > a:nth-child(1)", { "href": |DOC_PATH| + "/src/test_docs/lib.rs.html#1" @@ -122,7 +122,7 @@ store-property: ( ) define-function: ( "check-sidebar-dir-entry", - (x, y), + [x, y], block { assert: "details:first-of-type.dir-entry[open] > summary::marker" assert-css: ("#src-sidebar > details:first-of-type.dir-entry", {"padding-left": "4px"}) diff --git a/tests/rustdoc-gui/stab-badge.goml b/tests/rustdoc-gui/stab-badge.goml index bb3d2aaa3dca0..46df0946c4592 100644 --- a/tests/rustdoc-gui/stab-badge.goml +++ b/tests/rustdoc-gui/stab-badge.goml @@ -3,7 +3,7 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" show-text: true define-function: ( "check-badge", - (theme, background, color), + [theme, background, color], block { set-local-storage: {"rustdoc-use-system-theme": "false", "rustdoc-theme": |theme|} go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" diff --git a/tests/rustdoc-gui/target.goml b/tests/rustdoc-gui/target.goml index 26071df8d044e..0f8f770936356 100644 --- a/tests/rustdoc-gui/target.goml +++ b/tests/rustdoc-gui/target.goml @@ -7,7 +7,7 @@ assert: "#method\.a_method:target" define-function: ( "check-style", - (theme, background, border), + [theme, background, border], block { set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"} reload: diff --git a/tests/rustdoc-gui/toggle-docs.goml b/tests/rustdoc-gui/toggle-docs.goml index 9ea6d9b18f4c7..cfd18bd2e14f3 100644 --- a/tests/rustdoc-gui/toggle-docs.goml +++ b/tests/rustdoc-gui/toggle-docs.goml @@ -49,7 +49,7 @@ assert-attribute: ("details.toggle", {"open": ""}, ALL) show-text: true define-function: ( "check-color", - (theme, filter), + [theme, filter], block { // Setting the theme. set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"} diff --git a/tests/rustdoc-gui/type-declation-overflow.goml b/tests/rustdoc-gui/type-declation-overflow.goml index a97cc98897ae2..3709aa102661f 100644 --- a/tests/rustdoc-gui/type-declation-overflow.goml +++ b/tests/rustdoc-gui/type-declation-overflow.goml @@ -47,18 +47,18 @@ assert-css: (".mobile-topbar h2", {"overflow-x": "hidden"}) // On desktop, they wrap when too big. set-window-size: (1100, 800) go-to: "file://" + |DOC_PATH| + "/lib2/too_long/struct.SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName.html" -compare-elements-position-false: (".main-heading h1", ".main-heading .out-of-band", ("y")) +compare-elements-position-false: (".main-heading h1", ".main-heading .out-of-band", ["y"]) go-to: "file://" + |DOC_PATH| + "/lib2/index.html" -compare-elements-position: (".main-heading h1", ".main-heading .out-of-band", ("y")) +compare-elements-position: (".main-heading h1", ".main-heading .out-of-band", ["y"]) // make sure there is a gap between them compare-elements-position-near-false: (".main-heading h1", ".main-heading .out-of-band", {"x": 550}) // On mobile, they always wrap. set-window-size: (600, 600) go-to: "file://" + |DOC_PATH| + "/lib2/too_long/struct.SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName.html" -compare-elements-position-false: (".main-heading h1", ".main-heading .out-of-band", ("y")) +compare-elements-position-false: (".main-heading h1", ".main-heading .out-of-band", ["y"]) go-to: "file://" + |DOC_PATH| + "/lib2/index.html" -compare-elements-position-false: (".main-heading h1", ".main-heading .out-of-band", ("y")) +compare-elements-position-false: (".main-heading h1", ".main-heading .out-of-band", ["y"]) // Now we will check that the scrolling is working. // First on an item with "hidden methods". diff --git a/tests/rustdoc-gui/unsafe-fn.goml b/tests/rustdoc-gui/unsafe-fn.goml index 8d26f15f37f1b..83503121a0469 100644 --- a/tests/rustdoc-gui/unsafe-fn.goml +++ b/tests/rustdoc-gui/unsafe-fn.goml @@ -13,7 +13,7 @@ define-function: ( "sup-check", // `theme` is the theme being tested. // `color` is the expected color of the `` element. - (theme, color), + [theme, color], block { // Set the theme. set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"} @@ -23,6 +23,15 @@ define-function: ( }, ) -call-function: ("sup-check", ("ayu", "#c5c5c5")) -call-function: ("sup-check", ("dark", "#ddd")) -call-function: ("sup-check", ("light", "black")) +call-function: ("sup-check", { + "theme": "ayu", + "color": "#c5c5c5", +}) +call-function: ("sup-check", { + "theme": "dark", + "color": "#ddd", +}) +call-function: ("sup-check", { + "theme": "light", + "color": "black", +}) diff --git a/tests/rustdoc-gui/warning-block.goml b/tests/rustdoc-gui/warning-block.goml index 10e206049f53c..a5a47f868db22 100644 --- a/tests/rustdoc-gui/warning-block.goml +++ b/tests/rustdoc-gui/warning-block.goml @@ -5,7 +5,7 @@ show-text: true store-value: (default_y_pos, 5) define-function: ( "check-warning", - (theme, color, border_color), + [theme, color, border_color], block { set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"} reload: diff --git a/tests/rustdoc-gui/where-whitespace.goml b/tests/rustdoc-gui/where-whitespace.goml index da104fa40116f..823ce9704076b 100644 --- a/tests/rustdoc-gui/where-whitespace.goml +++ b/tests/rustdoc-gui/where-whitespace.goml @@ -3,25 +3,25 @@ go-to: "file://" + |DOC_PATH| + "/lib2/trait.Whitespace.html" show-text: true // First, we check in the trait definition if the where clause is "on its own" (not on the same // line than "pub trait Whitespace"). -compare-elements-position-false: (".item-decl code", "div.where", ("y")) +compare-elements-position-false: (".item-decl code", "div.where", ["y"]) // And that the code following it isn't on the same line either. -compare-elements-position-false: (".item-decl .fn", "div.where", ("y")) +compare-elements-position-false: (".item-decl .fn", "div.where", ["y"]) go-to: "file://" + |DOC_PATH| + "/lib2/struct.WhereWhitespace.html" // We make the screen a bit wider to ensure that the trait impl is on one line. set-window-size: (915, 915) -compare-elements-position-false: ("#method\.new .fn", "#method\.new div.where", ("y")) +compare-elements-position-false: ("#method\.new .fn", "#method\.new div.where", ["y"]) // We ensure that both the trait name and the struct name are on the same line in // "impl Whitespace<&K> for WhereWhitespace". compare-elements-position: ( "#trait-implementations-list .impl h3 .trait", "#trait-implementations-list .impl h3 .struct", - ("y"), + ["y"], ) // And we now check that the where condition isn't on the same line. compare-elements-position-false: ( "#trait-implementations-list .impl h3 .trait", "#trait-implementations-list .impl h3 div.where", - ("y"), + ["y"], ) diff --git a/tests/rustdoc-ui/synthetic-auto-trait-impls/const-in-super-trait-and-item-bound.rs b/tests/rustdoc-ui/synthetic-auto-trait-impls/const-in-super-trait-and-item-bound.rs new file mode 100644 index 0000000000000..df6de6769d57f --- /dev/null +++ b/tests/rustdoc-ui/synthetic-auto-trait-impls/const-in-super-trait-and-item-bound.rs @@ -0,0 +1,23 @@ +// We used to ICE here while trying to synthesize auto trait impls. +// issue: 107715 +//@ check-pass + +pub const N: usize = 1; + +pub struct MapType, V> { + _array: K::Array, +} + +pub trait Subtrait: Supertrait<[u8; N]> {} + +pub trait Supertrait { + type Array: AnotherTrait; +} + +pub trait AnotherTrait { + const LENGTH: usize; +} + +pub struct Container { + _x: MapType, +} diff --git a/tests/rustdoc-ui/synthetic-auto-trait-impls/lifetime-generic-user-impl-normalize.rs b/tests/rustdoc-ui/synthetic-auto-trait-impls/lifetime-generic-user-impl-normalize.rs new file mode 100644 index 0000000000000..1b67c2bc8756b --- /dev/null +++ b/tests/rustdoc-ui/synthetic-auto-trait-impls/lifetime-generic-user-impl-normalize.rs @@ -0,0 +1,17 @@ +// We used to ICE here while trying to synthesize auto trait impls. +// issue: 112242 +//@ check-pass +//@ compile-flags: -Znormalize-docs + +pub trait MyTrait<'a> { + type MyItem; +} +pub struct Inner(Q); +pub struct Outer(Inner); + +impl<'a, Q> std::marker::Unpin for Inner +where + Q: MyTrait<'a>, + >::MyItem: Copy, +{ +} diff --git a/tests/rustdoc-ui/synthetic-auto-trait-impls/lifetime-generic-user-impl.rs b/tests/rustdoc-ui/synthetic-auto-trait-impls/lifetime-generic-user-impl.rs new file mode 100644 index 0000000000000..31d1b11ff310e --- /dev/null +++ b/tests/rustdoc-ui/synthetic-auto-trait-impls/lifetime-generic-user-impl.rs @@ -0,0 +1,11 @@ +// We used to ICE here while trying to synthesize auto trait impls. +// issue: 123370 +//@ check-pass + +pub struct Inner<'a, Q>(&'a (), Q); + +pub struct Outer<'a, Q>(Inner<'a, Q>); + +impl<'a, Q: Trait<'a>> std::marker::Unpin for Inner<'static, Q> {} + +pub trait Trait<'a> {} diff --git a/tests/rustdoc-ui/synthetic-auto-trait-impls/projections-in-super-trait-bound-unsatisfied.rs b/tests/rustdoc-ui/synthetic-auto-trait-impls/projections-in-super-trait-bound-unsatisfied.rs new file mode 100644 index 0000000000000..f62f8396e9911 --- /dev/null +++ b/tests/rustdoc-ui/synthetic-auto-trait-impls/projections-in-super-trait-bound-unsatisfied.rs @@ -0,0 +1,18 @@ +// We used to ICE here while trying to synthesize auto trait impls. +// issue: 114657 + +pub trait Foo { + type FooType; +} + +pub trait Bar: Foo>::BarType> { + type BarType; +} + +pub(crate) const B: usize = 5; + +pub trait Tec: Bar {} + +pub struct Structure { //~ ERROR the trait bound `C: Bar<5>` is not satisfied + _field: C::BarType, //~ ERROR the trait bound `C: Bar<5>` is not satisfied +} diff --git a/tests/rustdoc-ui/synthetic-auto-trait-impls/projections-in-super-trait-bound-unsatisfied.stderr b/tests/rustdoc-ui/synthetic-auto-trait-impls/projections-in-super-trait-bound-unsatisfied.stderr new file mode 100644 index 0000000000000..d87e769b50538 --- /dev/null +++ b/tests/rustdoc-ui/synthetic-auto-trait-impls/projections-in-super-trait-bound-unsatisfied.stderr @@ -0,0 +1,25 @@ +error[E0277]: the trait bound `C: Bar<5>` is not satisfied + --> $DIR/projections-in-super-trait-bound-unsatisfied.rs:16:1 + | +LL | pub struct Structure { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Bar<5>` is not implemented for `C` + | +help: consider further restricting this bound + | +LL | pub struct Structure> { + | ++++++++ + +error[E0277]: the trait bound `C: Bar<5>` is not satisfied + --> $DIR/projections-in-super-trait-bound-unsatisfied.rs:17:13 + | +LL | _field: C::BarType, + | ^^^^^^^^^^ the trait `Bar<5>` is not implemented for `C` + | +help: consider further restricting this bound + | +LL | pub struct Structure> { + | ++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/rustdoc-ui/synthetic-auto-trait-impls/unconstrained-param-in-impl-ambiguity.rs b/tests/rustdoc-ui/synthetic-auto-trait-impls/unconstrained-param-in-impl-ambiguity.rs new file mode 100644 index 0000000000000..6c62415e06d56 --- /dev/null +++ b/tests/rustdoc-ui/synthetic-auto-trait-impls/unconstrained-param-in-impl-ambiguity.rs @@ -0,0 +1,10 @@ +// We used to ICE here while trying to synthesize auto trait impls. +// issue: 112828 + +struct Outer(Inner); +struct Inner; + +unsafe impl Send for Inner {} +//~^ ERROR the type parameter `Q` is not constrained by the impl trait, self type, or predicates + +trait Trait {} diff --git a/tests/rustdoc-ui/synthetic-auto-trait-impls/unconstrained-param-in-impl-ambiguity.stderr b/tests/rustdoc-ui/synthetic-auto-trait-impls/unconstrained-param-in-impl-ambiguity.stderr new file mode 100644 index 0000000000000..38d1a537fe458 --- /dev/null +++ b/tests/rustdoc-ui/synthetic-auto-trait-impls/unconstrained-param-in-impl-ambiguity.stderr @@ -0,0 +1,9 @@ +error[E0207]: the type parameter `Q` is not constrained by the impl trait, self type, or predicates + --> $DIR/unconstrained-param-in-impl-ambiguity.rs:7:13 + | +LL | unsafe impl Send for Inner {} + | ^ unconstrained type parameter + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0207`. diff --git a/tests/rustdoc/search-index-summaries.rs b/tests/rustdoc/search-index-summaries.rs index efd366405bfeb..529b42d0ca903 100644 --- a/tests/rustdoc/search-index-summaries.rs +++ b/tests/rustdoc/search-index-summaries.rs @@ -1,6 +1,6 @@ #![crate_name = "foo"] -// @hasraw 'search-index.js' 'Foo short link.' +// @hasraw 'search.desc/foo/foo-desc-0-.js' 'Foo short link.' // @!hasraw - 'www.example.com' // @!hasraw - 'More Foo.' diff --git a/tests/rustdoc/synthetic_auto/bounds.rs b/tests/rustdoc/synthetic_auto/bounds.rs new file mode 100644 index 0000000000000..17528d01c8d28 --- /dev/null +++ b/tests/rustdoc/synthetic_auto/bounds.rs @@ -0,0 +1,21 @@ +pub struct Outer(Inner); +pub struct Inner(T); + +// @has bounds/struct.Outer.html +// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \ +// "impl Unpin for Outerwhere \ +// T: for<'any> Trait = (), X = ()>," + +impl std::marker::Unpin for Inner +where + T: for<'any> Trait = (), X = ()>, +{} + +pub trait Trait: SuperTrait { + type A; + type B<'a>; +} + +pub trait SuperTrait { + type X; +} diff --git a/tests/rustdoc/synthetic_auto/complex.rs b/tests/rustdoc/synthetic_auto/complex.rs index 4c39f0bf1e07f..2722f6d338fe5 100644 --- a/tests/rustdoc/synthetic_auto/complex.rs +++ b/tests/rustdoc/synthetic_auto/complex.rs @@ -21,8 +21,8 @@ mod foo { // @has complex/struct.NotOuter.html // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \ -// "impl<'a, T, K: ?Sized> Send for Outer<'a, T, K>where K: for<'b> Fn((&'b bool, &'a u8)) \ -// -> &'b i8, T: MyTrait<'a>, >::MyItem: Copy, 'a: 'static" +// "impl<'a, T, K> Send for Outer<'a, T, K>where 'a: 'static, T: MyTrait<'a>, \ +// K: for<'b> Fn((&'b bool, &'a u8)) -> &'b i8 + ?Sized, >::MyItem: Copy," pub use foo::{Foo, Inner as NotInner, MyTrait as NotMyTrait, Outer as NotOuter}; diff --git a/tests/rustdoc/synthetic_auto/lifetimes.rs b/tests/rustdoc/synthetic_auto/lifetimes.rs index 71265b3078a06..23e1efdaeef19 100644 --- a/tests/rustdoc/synthetic_auto/lifetimes.rs +++ b/tests/rustdoc/synthetic_auto/lifetimes.rs @@ -10,7 +10,7 @@ where // @has lifetimes/struct.Foo.html // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \ -// "impl<'c, K> Send for Foo<'c, K>where K: for<'b> Fn(&'b bool) -> &'c u8, 'c: 'static" +// "impl<'c, K> Send for Foo<'c, K>where 'c: 'static, K: for<'b> Fn(&'b bool) -> &'c u8," // // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \ // "impl<'c, K> Sync for Foo<'c, K>where K: Sync" diff --git a/tests/rustdoc/synthetic_auto/no-redundancy.rs b/tests/rustdoc/synthetic_auto/no-redundancy.rs index d30b38dd4dc1d..64dab429647ae 100644 --- a/tests/rustdoc/synthetic_auto/no-redundancy.rs +++ b/tests/rustdoc/synthetic_auto/no-redundancy.rs @@ -1,6 +1,3 @@ -// FIXME(fmease, #119216): Reenable this test! -//@ ignore-test - pub struct Inner { field: T, } @@ -13,7 +10,7 @@ where // @has no_redundancy/struct.Outer.html // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \ -// "impl Send for Outerwhere T: Send + Copy" +// "impl Send for Outerwhere T: Copy + Send" pub struct Outer { inner_field: Inner, } diff --git a/tests/rustdoc/synthetic_auto/project.rs b/tests/rustdoc/synthetic_auto/project.rs index 7c9412ae96243..f4ede76e6debf 100644 --- a/tests/rustdoc/synthetic_auto/project.rs +++ b/tests/rustdoc/synthetic_auto/project.rs @@ -24,11 +24,11 @@ where // @has project/struct.Foo.html // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \ -// "impl<'c, K> Send for Foo<'c, K>where K: MyTrait, 'c: 'static" +// "impl<'c, K> Send for Foo<'c, K>where 'c: 'static, K: MyTrait," // // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \ -// "impl<'c, K> Sync for Foo<'c, K>where K: MyTrait, ::MyItem: OtherTrait, \ -// 'c: 'static," +// "impl<'c, K> Sync for Foo<'c, K>where 'c: 'static, K: MyTrait, \ +// ::MyItem: OtherTrait," pub struct Foo<'c, K: 'c> { inner_field: Inner<'c, K>, } diff --git a/tests/ui-fulldeps/stable-mir/check_normalization.rs b/tests/ui-fulldeps/stable-mir/check_normalization.rs new file mode 100644 index 0000000000000..72e410f80809b --- /dev/null +++ b/tests/ui-fulldeps/stable-mir/check_normalization.rs @@ -0,0 +1,95 @@ +//@ run-pass +//! Test that types are normalized in an instance body. + +//@ ignore-stage1 +//@ ignore-cross-compile +//@ ignore-remote +//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 +//@ edition: 2021 + +#![feature(rustc_private)] + +#[macro_use] +extern crate rustc_smir; +extern crate rustc_driver; +extern crate rustc_interface; +extern crate stable_mir; + +use mir::mono::Instance; +use ty::{Ty, TyKind, RigidTy}; +use rustc_smir::rustc_internal; +use stable_mir::*; +use std::io::Write; +use std::ops::ControlFlow; + +const CRATE_NAME: &str = "input"; + +/// This function uses the Stable MIR APIs to get information about the test crate. +fn test_stable_mir() -> ControlFlow<()> { + let items = stable_mir::all_local_items(); + + // Get all items and split generic vs monomorphic items. + let instances: Vec<_> = + items.into_iter().filter_map(|item| (!item.requires_monomorphization()).then(|| { + Instance::try_from(item).unwrap() + })).collect(); + assert_eq!(instances.len(), 1, "Expected one constant"); + + for instance in instances { + check_ty(instance.ty()); + } + ControlFlow::Continue(()) +} + +fn check_ty(ty: Ty) { + match ty.kind() { + TyKind::RigidTy(RigidTy::Adt(def, args)) if def.kind().is_struct() => { + // Ensure field type is also normalized + def.variants_iter().next().unwrap().fields().into_iter().for_each(|f| { + check_ty(f.ty_with_args(&args)) + }); + } + TyKind::RigidTy(RigidTy::Uint(..)) => {} + kind => unreachable!("Unexpected kind: {kind:?}") + } +} + + +/// This test will generate and analyze a dummy crate using the stable mir. +/// For that, it will first write the dummy crate into a file. +/// Then it will create a `StableMir` using custom arguments and then +/// it will run the compiler. +fn main() { + let path = "normalization_input.rs"; + generate_input(&path).unwrap(); + let args = vec![ + "rustc".to_string(), + "-Cpanic=abort".to_string(), + "--crate-type=lib".to_string(), + "--crate-name".to_string(), + CRATE_NAME.to_string(), + path.to_string(), + ]; + run!(args, test_stable_mir).unwrap(); +} + +fn generate_input(path: &str) -> std::io::Result<()> { + let mut file = std::fs::File::create(path)?; + write!( + file, + r#" + pub trait Primitive {{ + type Base; + }} + + impl Primitive for char {{ + type Base = u32; + }} + + pub struct Wrapper(T::Base); + pub type WrapperChar = Wrapper; + pub const NULL_CHAR: WrapperChar = Wrapper::(0); + "# + )?; + Ok(()) +} diff --git a/tests/ui/abi/extern/extern-pass-FiveU16s.rs b/tests/ui/abi/extern/extern-pass-FiveU16s.rs new file mode 100644 index 0000000000000..5f1307beb28e6 --- /dev/null +++ b/tests/ui/abi/extern/extern-pass-FiveU16s.rs @@ -0,0 +1,30 @@ +//@ run-pass +#![allow(improper_ctypes)] + +// Test a foreign function that accepts and returns a struct by value. + +// FiveU16s in particular is interesting because it is larger than a single 64 bit or 32 bit +// register, which are used as cast destinations on some targets, but does not evenly divide those +// sizes, causing there to be padding in the last element. + +#[derive(Copy, Clone, PartialEq, Debug)] +pub struct FiveU16s { + one: u16, + two: u16, + three: u16, + four: u16, + five: u16, +} + +#[link(name = "rust_test_helpers", kind = "static")] +extern "C" { + pub fn rust_dbg_extern_identity_FiveU16s(v: FiveU16s) -> FiveU16s; +} + +pub fn main() { + unsafe { + let x = FiveU16s { one: 22, two: 23, three: 24, four: 25, five: 26 }; + let y = rust_dbg_extern_identity_FiveU16s(x); + assert_eq!(x, y); + } +} diff --git a/tests/ui/abi/extern/extern-return-FiveU16s.rs b/tests/ui/abi/extern/extern-return-FiveU16s.rs new file mode 100644 index 0000000000000..d8ae8b2661c5a --- /dev/null +++ b/tests/ui/abi/extern/extern-return-FiveU16s.rs @@ -0,0 +1,26 @@ +//@ run-pass +#![allow(improper_ctypes)] + +pub struct FiveU16s { + one: u16, + two: u16, + three: u16, + four: u16, + five: u16, +} + +#[link(name = "rust_test_helpers", kind = "static")] +extern "C" { + pub fn rust_dbg_extern_return_FiveU16s() -> FiveU16s; +} + +pub fn main() { + unsafe { + let y = rust_dbg_extern_return_FiveU16s(); + assert_eq!(y.one, 10); + assert_eq!(y.two, 20); + assert_eq!(y.three, 30); + assert_eq!(y.four, 40); + assert_eq!(y.five, 50); + } +} diff --git a/tests/ui/asm/x86_64/goto.rs b/tests/ui/asm/x86_64/goto.rs index 6a567efbb2c7e..6c14bb57ac6e2 100644 --- a/tests/ui/asm/x86_64/goto.rs +++ b/tests/ui/asm/x86_64/goto.rs @@ -1,8 +1,6 @@ //@ only-x86_64 //@ run-pass //@ needs-asm-support -//@ revisions: mirunsafeck thirunsafeck -//@ [thirunsafeck]compile-flags: -Z thir-unsafeck #![deny(unreachable_code)] #![feature(asm_goto)] diff --git a/tests/ui/asm/x86_64/goto.mirunsafeck.stderr b/tests/ui/asm/x86_64/goto.stderr similarity index 92% rename from tests/ui/asm/x86_64/goto.mirunsafeck.stderr rename to tests/ui/asm/x86_64/goto.stderr index fe189c14f0a7c..27e227d71a5f5 100644 --- a/tests/ui/asm/x86_64/goto.mirunsafeck.stderr +++ b/tests/ui/asm/x86_64/goto.stderr @@ -1,5 +1,5 @@ warning: unreachable statement - --> $DIR/goto.rs:99:9 + --> $DIR/goto.rs:97:9 | LL | / asm!( LL | | "jmp {}", @@ -13,7 +13,7 @@ LL | unreachable!(); | ^^^^^^^^^^^^^^ unreachable statement | note: the lint level is defined here - --> $DIR/goto.rs:89:8 + --> $DIR/goto.rs:87:8 | LL | #[warn(unreachable_code)] | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/asm/x86_64/goto.thirunsafeck.stderr b/tests/ui/asm/x86_64/goto.thirunsafeck.stderr deleted file mode 100644 index fe189c14f0a7c..0000000000000 --- a/tests/ui/asm/x86_64/goto.thirunsafeck.stderr +++ /dev/null @@ -1,23 +0,0 @@ -warning: unreachable statement - --> $DIR/goto.rs:99:9 - | -LL | / asm!( -LL | | "jmp {}", -LL | | label { -LL | | return; -LL | | }, -LL | | options(noreturn) -LL | | ); - | |_________- any code following this expression is unreachable -LL | unreachable!(); - | ^^^^^^^^^^^^^^ unreachable statement - | -note: the lint level is defined here - --> $DIR/goto.rs:89:8 - | -LL | #[warn(unreachable_code)] - | ^^^^^^^^^^^^^^^^ - = note: this warning originates in the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) - -warning: 1 warning emitted - diff --git a/tests/ui/async-await/async-closures/captures.rs b/tests/ui/async-await/async-closures/captures.rs new file mode 100644 index 0000000000000..0a9d0529bf542 --- /dev/null +++ b/tests/ui/async-await/async-closures/captures.rs @@ -0,0 +1,116 @@ +//@ aux-build:block-on.rs +//@ edition:2021 +//@ run-pass +//@ check-run-results + +// Same as miri's `tests/pass/async-closure-captures.rs`, keep in sync + +#![feature(async_closure)] + +extern crate block_on; + +fn main() { + block_on::block_on(async_main()); +} + +async fn call(f: &impl async Fn() -> T) -> T { + f().await +} + +async fn call_once(f: impl async FnOnce() -> T) -> T { + f().await +} + +#[derive(Debug)] +#[allow(unused)] +struct Hello(i32); + +async fn async_main() { + // Capture something by-ref + { + let x = Hello(0); + let c = async || { + println!("{x:?}"); + }; + call(&c).await; + call_once(c).await; + + let x = &Hello(1); + let c = async || { + println!("{x:?}"); + }; + call(&c).await; + call_once(c).await; + } + + // Capture something and consume it (force to `AsyncFnOnce`) + { + let x = Hello(2); + let c = async || { + println!("{x:?}"); + drop(x); + }; + call_once(c).await; + } + + // Capture something with `move`, don't consume it + { + let x = Hello(3); + let c = async move || { + println!("{x:?}"); + }; + call(&c).await; + call_once(c).await; + + let x = &Hello(4); + let c = async move || { + println!("{x:?}"); + }; + call(&c).await; + call_once(c).await; + } + + // Capture something with `move`, also consume it (so `AsyncFnOnce`) + { + let x = Hello(5); + let c = async move || { + println!("{x:?}"); + drop(x); + }; + call_once(c).await; + } + + fn force_fnonce(f: impl async FnOnce() -> T) -> impl async FnOnce() -> T { + f + } + + // Capture something with `move`, but infer to `AsyncFnOnce` + { + let x = Hello(6); + let c = force_fnonce(async move || { + println!("{x:?}"); + }); + call_once(c).await; + + let x = &Hello(7); + let c = force_fnonce(async move || { + println!("{x:?}"); + }); + call_once(c).await; + } + + // Capture something by-ref, but infer to `AsyncFnOnce` + { + let x = Hello(8); + let c = force_fnonce(async || { + println!("{x:?}"); + }); + call_once(c).await; + + let x = &Hello(9); + let c = force_fnonce(async || { + println!("{x:?}"); + }); + call_once(c).await; + } +} diff --git a/tests/ui/async-await/async-closures/captures.run.stdout b/tests/ui/async-await/async-closures/captures.run.stdout new file mode 100644 index 0000000000000..42a7999b2dcdd --- /dev/null +++ b/tests/ui/async-await/async-closures/captures.run.stdout @@ -0,0 +1,14 @@ +Hello(0) +Hello(0) +Hello(1) +Hello(1) +Hello(2) +Hello(3) +Hello(3) +Hello(4) +Hello(4) +Hello(5) +Hello(6) +Hello(7) +Hello(8) +Hello(9) diff --git a/tests/ui/async-await/async-closures/wrong-fn-kind.rs b/tests/ui/async-await/async-closures/wrong-fn-kind.rs index 8502bb6e2d45d..3d6f856874f2c 100644 --- a/tests/ui/async-await/async-closures/wrong-fn-kind.rs +++ b/tests/ui/async-await/async-closures/wrong-fn-kind.rs @@ -2,18 +2,22 @@ #![feature(async_closure)] -fn main() { - fn needs_async_fn(_: impl async Fn()) {} +fn needs_async_fn(_: impl async Fn()) {} +fn a() { let mut x = 1; needs_async_fn(async || { - //~^ ERROR expected a closure that implements the `async Fn` trait, but this closure only implements `async FnMut` + //~^ ERROR cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure x += 1; }); +} +fn b() { let x = String::new(); needs_async_fn(move || async move { //~^ ERROR expected a closure that implements the `async Fn` trait, but this closure only implements `async FnOnce` println!("{x}"); }); } + +fn main() {} diff --git a/tests/ui/async-await/async-closures/wrong-fn-kind.stderr b/tests/ui/async-await/async-closures/wrong-fn-kind.stderr index d0f1948e48f83..e56389b320273 100644 --- a/tests/ui/async-await/async-closures/wrong-fn-kind.stderr +++ b/tests/ui/async-await/async-closures/wrong-fn-kind.stderr @@ -1,26 +1,5 @@ -error[E0525]: expected a closure that implements the `async Fn` trait, but this closure only implements `async FnMut` - --> $DIR/wrong-fn-kind.rs:9:20 - | -LL | needs_async_fn(async || { - | -------------- -^^^^^^^ - | | | - | _____|______________this closure implements `async FnMut`, not `async Fn` - | | | - | | required by a bound introduced by this call -LL | | -LL | | x += 1; - | | - closure is `async FnMut` because it mutates the variable `x` here -LL | | }); - | |_____- the requirement to implement `async Fn` derives from here - | -note: required by a bound in `needs_async_fn` - --> $DIR/wrong-fn-kind.rs:6:31 - | -LL | fn needs_async_fn(_: impl async Fn()) {} - | ^^^^^^^^^^ required by this bound in `needs_async_fn` - error[E0525]: expected a closure that implements the `async Fn` trait, but this closure only implements `async FnOnce` - --> $DIR/wrong-fn-kind.rs:15:20 + --> $DIR/wrong-fn-kind.rs:17:20 | LL | needs_async_fn(move || async move { | -------------- -^^^^^^ @@ -35,11 +14,29 @@ LL | | }); | |_____- the requirement to implement `async Fn` derives from here | note: required by a bound in `needs_async_fn` - --> $DIR/wrong-fn-kind.rs:6:31 + --> $DIR/wrong-fn-kind.rs:5:27 + | +LL | fn needs_async_fn(_: impl async Fn()) {} + | ^^^^^^^^^^ required by this bound in `needs_async_fn` + +error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure + --> $DIR/wrong-fn-kind.rs:9:29 | -LL | fn needs_async_fn(_: impl async Fn()) {} - | ^^^^^^^^^^ required by this bound in `needs_async_fn` +LL | fn needs_async_fn(_: impl async Fn()) {} + | --------------- change this to accept `FnMut` instead of `Fn` +... +LL | needs_async_fn(async || { + | _____--------------_--------_^ + | | | | + | | | in this closure + | | expects `Fn` instead of `FnMut` +LL | | +LL | | x += 1; + | | - mutable borrow occurs due to use of `x` in closure +LL | | }); + | |_____^ cannot borrow as mutable error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0525`. +Some errors have detailed explanations: E0525, E0596. +For more information about an error, try `rustc --explain E0525`. diff --git a/tests/ui/async-await/async-is-unwindsafe.rs b/tests/ui/async-await/async-is-unwindsafe.rs index 53009b6e7410f..d0202f72f0086 100644 --- a/tests/ui/async-await/async-is-unwindsafe.rs +++ b/tests/ui/async-await/async-is-unwindsafe.rs @@ -11,6 +11,7 @@ fn main() { is_unwindsafe(async { //~^ ERROR the type `&mut Context<'_>` may not be safely transferred across an unwind boundary + //~| ERROR the type `&mut (dyn Any + 'static)` may not be safely transferred across an unwind boundary use std::ptr::null; use std::task::{Context, RawWaker, RawWakerVTable, Waker}; let waker = unsafe { diff --git a/tests/ui/async-await/async-is-unwindsafe.stderr b/tests/ui/async-await/async-is-unwindsafe.stderr index 5d87fc747682a..6bb06df9f39e9 100644 --- a/tests/ui/async-await/async-is-unwindsafe.stderr +++ b/tests/ui/async-await/async-is-unwindsafe.stderr @@ -6,19 +6,18 @@ LL | is_unwindsafe(async { | |_____| | || LL | || +LL | || LL | || use std::ptr::null; -LL | || use std::task::{Context, RawWaker, RawWakerVTable, Waker}; ... || LL | || drop(cx_ref); LL | || }); | ||_____-^ `&mut Context<'_>` may not be safely transferred across an unwind boundary | |_____| - | within this `{async block@$DIR/async-is-unwindsafe.rs:12:19: 29:6}` + | within this `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}` | - = help: within `{async block@$DIR/async-is-unwindsafe.rs:12:19: 29:6}`, the trait `UnwindSafe` is not implemented for `&mut Context<'_>`, which is required by `{async block@$DIR/async-is-unwindsafe.rs:12:19: 29:6}: UnwindSafe` - = note: `UnwindSafe` is implemented for `&Context<'_>`, but not for `&mut Context<'_>` + = help: within `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}`, the trait `UnwindSafe` is not implemented for `&mut Context<'_>`, which is required by `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}: UnwindSafe` note: future does not implement `UnwindSafe` as this value is used across an await - --> $DIR/async-is-unwindsafe.rs:25:18 + --> $DIR/async-is-unwindsafe.rs:26:18 | LL | let cx_ref = &mut cx; | ------ has type `&mut Context<'_>` which does not implement `UnwindSafe` @@ -31,6 +30,38 @@ note: required by a bound in `is_unwindsafe` LL | fn is_unwindsafe(_: impl std::panic::UnwindSafe) {} | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_unwindsafe` -error: aborting due to 1 previous error +error[E0277]: the type `&mut (dyn Any + 'static)` may not be safely transferred across an unwind boundary + --> $DIR/async-is-unwindsafe.rs:12:5 + | +LL | is_unwindsafe(async { + | _____^_____________- + | |_____| + | || +LL | || +LL | || +LL | || use std::ptr::null; +... || +LL | || drop(cx_ref); +LL | || }); + | ||_____-^ `&mut (dyn Any + 'static)` may not be safely transferred across an unwind boundary + | |_____| + | within this `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}` + | + = help: within `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}`, the trait `UnwindSafe` is not implemented for `&mut (dyn Any + 'static)`, which is required by `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}: UnwindSafe` +note: future does not implement `UnwindSafe` as this value is used across an await + --> $DIR/async-is-unwindsafe.rs:26:18 + | +LL | let mut cx = Context::from_waker(&waker); + | ------ has type `Context<'_>` which does not implement `UnwindSafe` +... +LL | async {}.await; // this needs an inner await point + | ^^^^^ await occurs here, with `mut cx` maybe used later +note: required by a bound in `is_unwindsafe` + --> $DIR/async-is-unwindsafe.rs:3:26 + | +LL | fn is_unwindsafe(_: impl std::panic::UnwindSafe) {} + | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_unwindsafe` + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/async-await/coroutine-desc.stderr b/tests/ui/async-await/coroutine-desc.stderr index e4cb0915a1090..1f1e303ea4c67 100644 --- a/tests/ui/async-await/coroutine-desc.stderr +++ b/tests/ui/async-await/coroutine-desc.stderr @@ -5,6 +5,7 @@ LL | fun(async {}, async {}); | --- -------- ^^^^^^^^ expected `async` block, found a different `async` block | | | | | the expected `async` block + | | expected all arguments to be this `async` block type because they need to match the type of this parameter | arguments to this function are incorrect | = note: expected `async` block `{async block@$DIR/coroutine-desc.rs:10:9: 10:17}` @@ -13,14 +14,18 @@ note: function defined here --> $DIR/coroutine-desc.rs:8:4 | LL | fn fun>(f1: F, f2: F) {} - | ^^^ ----- + | ^^^ - ----- ----- this parameter needs to match the `async` block type of `f1` + | | | + | | `f2` needs to match the `async` block type of this parameter + | `f1` and `f2` all reference this parameter F error[E0308]: mismatched types --> $DIR/coroutine-desc.rs:12:16 | LL | fun(one(), two()); - | --- ^^^^^ expected future, found a different future - | | + | --- ----- ^^^^^ expected future, found a different future + | | | + | | expected all arguments to be this future type because they need to match the type of this parameter | arguments to this function are incorrect | = help: consider `await`ing on both `Future`s @@ -29,15 +34,19 @@ note: function defined here --> $DIR/coroutine-desc.rs:8:4 | LL | fn fun>(f1: F, f2: F) {} - | ^^^ ----- + | ^^^ - ----- ----- this parameter needs to match the future type of `f1` + | | | + | | `f2` needs to match the future type of this parameter + | `f1` and `f2` all reference this parameter F error[E0308]: mismatched types --> $DIR/coroutine-desc.rs:14:26 | LL | fun((async || {})(), (async || {})()); - | --- -- ^^^^^^^^^^^^^^^ expected `async` closure body, found a different `async` closure body - | | | - | | the expected `async` closure body + | --- --------------- ^^^^^^^^^^^^^^^ expected `async` closure body, found a different `async` closure body + | | | | + | | | the expected `async` closure body + | | expected all arguments to be this `async` closure body type because they need to match the type of this parameter | arguments to this function are incorrect | = note: expected `async` closure body `{async closure body@$DIR/coroutine-desc.rs:14:19: 14:21}` @@ -46,7 +55,10 @@ note: function defined here --> $DIR/coroutine-desc.rs:8:4 | LL | fn fun>(f1: F, f2: F) {} - | ^^^ ----- + | ^^^ - ----- ----- this parameter needs to match the `async` closure body type of `f1` + | | | + | | `f2` needs to match the `async` closure body type of this parameter + | `f1` and `f2` all reference this parameter F error: aborting due to 3 previous errors diff --git a/tests/ui/attributes/unix_sigpipe/auxiliary/assert-sigpipe-disposition.rs b/tests/ui/attributes/unix_sigpipe/auxiliary/assert-sigpipe-disposition.rs new file mode 100644 index 0000000000000..117f6134b4e03 --- /dev/null +++ b/tests/ui/attributes/unix_sigpipe/auxiliary/assert-sigpipe-disposition.rs @@ -0,0 +1,34 @@ +// It is UB to unwind out of `fn start()` according to +// https://doc.rust-lang.org/beta/unstable-book/language-features/start.html so +// panic with abort to avoid UB: +//@ compile-flags: -Cpanic=abort +//@ no-prefer-dynamic so panic=abort works + +#![feature(start, rustc_private)] + +extern crate libc; + +// Use #[start] so we don't have a runtime that messes with SIGPIPE. +#[start] +fn start(argc: isize, argv: *const *const u8) -> isize { + assert_eq!(argc, 2, "Must pass SIG_IGN or SIG_DFL as first arg"); + let arg1 = unsafe { std::ffi::CStr::from_ptr(*argv.offset(1) as *const libc::c_char) } + .to_str() + .unwrap(); + + let expected = match arg1 { + "SIG_IGN" => libc::SIG_IGN, + "SIG_DFL" => libc::SIG_DFL, + arg => panic!("Must pass SIG_IGN or SIG_DFL as first arg. Got: {}", arg), + }; + + let actual = unsafe { + let mut actual: libc::sigaction = std::mem::zeroed(); + libc::sigaction(libc::SIGPIPE, std::ptr::null(), &mut actual); + actual.sa_sigaction + }; + + assert_eq!(actual, expected, "actual and expected SIGPIPE disposition in child differs"); + + 0 +} diff --git a/tests/ui/attributes/unix_sigpipe/unix_sigpipe-and-child-processes.rs b/tests/ui/attributes/unix_sigpipe/unix_sigpipe-and-child-processes.rs new file mode 100644 index 0000000000000..f96bd634876fd --- /dev/null +++ b/tests/ui/attributes/unix_sigpipe/unix_sigpipe-and-child-processes.rs @@ -0,0 +1,56 @@ +//@ revisions: default sig_dfl sig_ign inherit +//@ ignore-cross-compile because aux-bin does not yet support it +//@ only-unix because SIGPIPE is a unix thing +//@ run-pass +//@ aux-bin:assert-sigpipe-disposition.rs +//@ aux-crate:sigpipe_utils=sigpipe-utils.rs + +// Checks the signal disposition of `SIGPIPE` in child processes, and in our own +// process for robustness. Without any `unix_sigpipe` attribute, `SIG_IGN` is +// the default. But there is a difference in how `SIGPIPE` is treated in child +// processes with and without the attribute. Search for +// `unix_sigpipe_attr_specified()` in the code base to learn more. + +#![feature(rustc_private)] +#![cfg_attr(any(sig_dfl, sig_ign, inherit), feature(unix_sigpipe))] + +extern crate libc; +extern crate sigpipe_utils; + +use sigpipe_utils::*; + +#[cfg_attr(sig_dfl, unix_sigpipe = "sig_dfl")] +#[cfg_attr(sig_ign, unix_sigpipe = "sig_ign")] +#[cfg_attr(inherit, unix_sigpipe = "inherit")] +fn main() { + // By default we get SIG_IGN but the child gets SIG_DFL through an explicit + // reset before exec: + // https://github.com/rust-lang/rust/blob/bf4de3a874753bbee3323081c8b0c133444fed2d/library/std/src/sys/pal/unix/process/process_unix.rs#L363-L384 + #[cfg(default)] + let (we_expect, child_expects) = (SignalHandler::Ignore, "SIG_DFL"); + + // With #[unix_sigpipe = "sig_dfl"] we get SIG_DFL and the child does too + // without any special code running before exec. + #[cfg(sig_dfl)] + let (we_expect, child_expects) = (SignalHandler::Default, "SIG_DFL"); + + // With #[unix_sigpipe = "sig_ign"] we get SIG_IGN and the child does too + // without any special code running before exec. + #[cfg(sig_ign)] + let (we_expect, child_expects) = (SignalHandler::Ignore, "SIG_IGN"); + + // With #[unix_sigpipe = "inherit"] we get SIG_DFL and the child does too + // without any special code running before exec. + #[cfg(inherit)] + let (we_expect, child_expects) = (SignalHandler::Default, "SIG_DFL"); + + assert_sigpipe_handler(we_expect); + + assert!( + std::process::Command::new("./auxiliary/bin/assert-sigpipe-disposition") + .arg(child_expects) + .status() + .unwrap() + .success() + ); +} diff --git a/tests/ui/binop/binary-op-suggest-deref.stderr b/tests/ui/binop/binary-op-suggest-deref.stderr index 32bd2554abb62..47af51e2106b2 100644 --- a/tests/ui/binop/binary-op-suggest-deref.stderr +++ b/tests/ui/binop/binary-op-suggest-deref.stderr @@ -247,15 +247,15 @@ LL | _ = &&0 == Foo; | = help: the trait `PartialEq` is not implemented for `&&{integer}` = help: the following other types implement trait `PartialEq`: + f128 + f16 f32 f64 i128 i16 i32 i64 - i8 - isize - and 6 others + and 8 others error[E0369]: binary operation `==` cannot be applied to type `Foo` --> $DIR/binary-op-suggest-deref.rs:60:13 diff --git a/tests/ui/issues/issue-1460.rs b/tests/ui/closures/issue-1460.rs similarity index 100% rename from tests/ui/issues/issue-1460.rs rename to tests/ui/closures/issue-1460.rs diff --git a/tests/ui/issues/issue-1460.stderr b/tests/ui/closures/issue-1460.stderr similarity index 100% rename from tests/ui/issues/issue-1460.stderr rename to tests/ui/closures/issue-1460.stderr diff --git a/tests/ui/coercion/coerce-reborrow-multi-arg-fail.stderr b/tests/ui/coercion/coerce-reborrow-multi-arg-fail.stderr index 498ef33d52ecc..46723c5a297f7 100644 --- a/tests/ui/coercion/coerce-reborrow-multi-arg-fail.stderr +++ b/tests/ui/coercion/coerce-reborrow-multi-arg-fail.stderr @@ -2,8 +2,9 @@ error[E0308]: mismatched types --> $DIR/coerce-reborrow-multi-arg-fail.rs:4:18 | LL | test(&mut 7, &7); - | ---- ^^ types differ in mutability - | | + | ---- ------ ^^ types differ in mutability + | | | + | | expected all arguments to be this `&mut {integer}` type because they need to match the type of this parameter | arguments to this function are incorrect | = note: expected mutable reference `&mut {integer}` @@ -12,7 +13,10 @@ note: function defined here --> $DIR/coerce-reborrow-multi-arg-fail.rs:1:4 | LL | fn test(_a: T, _b: T) {} - | ^^^^ ----- + | ^^^^ - ----- ----- this parameter needs to match the `&mut {integer}` type of `_a` + | | | + | | `_b` needs to match the `&mut {integer}` type of this parameter + | `_a` and `_b` all reference this parameter T error: aborting due to 1 previous error diff --git a/tests/ui/coherence/negative-coherence/regions-in-canonical.rs b/tests/ui/coherence/negative-coherence/regions-in-canonical.rs new file mode 100644 index 0000000000000..6c2a11e013583 --- /dev/null +++ b/tests/ui/coherence/negative-coherence/regions-in-canonical.rs @@ -0,0 +1,23 @@ +//@ check-pass + +#![feature(adt_const_params)] +//~^ WARN the feature `adt_const_params` is incomplete +#![feature(with_negative_coherence, negative_impls)] + +pub trait A {} +pub trait C {} + + +struct W(T); + +// Negative coherence: +// Proving `W: !A<"">` requires proving `CONST alias-eq ""`, which requires proving +// `CONST normalizes-to (?1c: &str)`. The type's region is uniquified, so it ends up being +// put in to the canonical vars list with an infer region => ICE. +impl C for T where T: A<""> {} +impl C for W {} + +impl !A for W {} +const CONST: &str = ""; + +fn main() {} diff --git a/tests/ui/coherence/negative-coherence/regions-in-canonical.stderr b/tests/ui/coherence/negative-coherence/regions-in-canonical.stderr new file mode 100644 index 0000000000000..dc8c926f182b4 --- /dev/null +++ b/tests/ui/coherence/negative-coherence/regions-in-canonical.stderr @@ -0,0 +1,11 @@ +warning: the feature `adt_const_params` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/regions-in-canonical.rs:3:12 + | +LL | #![feature(adt_const_params)] + | ^^^^^^^^^^^^^^^^ + | + = note: see issue #95174 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/coherence/skip-reporting-if-references-err.current.stderr b/tests/ui/coherence/skip-reporting-if-references-err.current.stderr new file mode 100644 index 0000000000000..5eef3256b2c36 --- /dev/null +++ b/tests/ui/coherence/skip-reporting-if-references-err.current.stderr @@ -0,0 +1,27 @@ +error[E0726]: implicit elided lifetime not allowed here + --> $DIR/skip-reporting-if-references-err.rs:10:9 + | +LL | impl ToUnit for T {} + | ^^^^^^ expected lifetime parameter + | +help: indicate the anonymous lifetime + | +LL | impl ToUnit<'_> for T {} + | ++++ + +error[E0277]: the trait bound `for<'a> (): ToUnit<'a>` is not satisfied + --> $DIR/skip-reporting-if-references-err.rs:15:29 + | +LL | impl Overlap for for<'a> fn(<() as ToUnit<'a>>::Unit) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'a> ToUnit<'a>` is not implemented for `()` + +error[E0277]: the trait bound `for<'a> (): ToUnit<'a>` is not satisfied + --> $DIR/skip-reporting-if-references-err.rs:15:18 + | +LL | impl Overlap for for<'a> fn(<() as ToUnit<'a>>::Unit) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'a> ToUnit<'a>` is not implemented for `()` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0277, E0726. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/coherence/skip-reporting-if-references-err.next.stderr b/tests/ui/coherence/skip-reporting-if-references-err.next.stderr new file mode 100644 index 0000000000000..5de4cf626e481 --- /dev/null +++ b/tests/ui/coherence/skip-reporting-if-references-err.next.stderr @@ -0,0 +1,14 @@ +error[E0726]: implicit elided lifetime not allowed here + --> $DIR/skip-reporting-if-references-err.rs:10:9 + | +LL | impl ToUnit for T {} + | ^^^^^^ expected lifetime parameter + | +help: indicate the anonymous lifetime + | +LL | impl ToUnit<'_> for T {} + | ++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0726`. diff --git a/tests/ui/coherence/skip-reporting-if-references-err.rs b/tests/ui/coherence/skip-reporting-if-references-err.rs new file mode 100644 index 0000000000000..f9eaa498232da --- /dev/null +++ b/tests/ui/coherence/skip-reporting-if-references-err.rs @@ -0,0 +1,19 @@ +// Regression test for #121006. +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +trait ToUnit<'a> { + type Unit; +} + +impl ToUnit for T {} +//~^ ERROR implicit elided lifetime not allowed here + +trait Overlap {} +impl Overlap for fn(U) {} +impl Overlap for for<'a> fn(<() as ToUnit<'a>>::Unit) {} +//[current]~^ ERROR the trait bound `for<'a> (): ToUnit<'a>` is not satisfied +//[current]~| ERROR the trait bound `for<'a> (): ToUnit<'a>` is not satisfied + +fn main() {} diff --git a/tests/ui/compiletest-self-test/test-aux-bin.rs b/tests/ui/compiletest-self-test/test-aux-bin.rs index 9e01e3ffabff1..c1c28e12b3b5a 100644 --- a/tests/ui/compiletest-self-test/test-aux-bin.rs +++ b/tests/ui/compiletest-self-test/test-aux-bin.rs @@ -1,4 +1,4 @@ -//@ ignore-cross-compile because we run the compiled code +//@ ignore-cross-compile because aux-bin does not yet support it //@ aux-bin: print-it-works.rs //@ run-pass diff --git a/tests/ui/const-generics/generic_const_exprs/opaque_type.rs b/tests/ui/const-generics/generic_const_exprs/opaque_type.rs new file mode 100644 index 0000000000000..56b8acbf88cde --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/opaque_type.rs @@ -0,0 +1,18 @@ +#![feature(generic_const_exprs, type_alias_impl_trait)] +#![allow(incomplete_features)] + +type Foo = impl Sized; + +fn with_bound() -> Foo +where + [u8; (N / 2) as usize]: Sized, +{ + let _: [u8; (N / 2) as Foo] = [0; (N / 2) as usize]; + //~^ ERROR mismatched types + //~| ERROR non-primitive cast: `usize` as `Foo` + todo!() +} + +fn main() { + with_bound::<4>(); +} diff --git a/tests/ui/const-generics/generic_const_exprs/opaque_type.stderr b/tests/ui/const-generics/generic_const_exprs/opaque_type.stderr new file mode 100644 index 0000000000000..e9fb8c0f403ae --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/opaque_type.stderr @@ -0,0 +1,22 @@ +error[E0308]: mismatched types + --> $DIR/opaque_type.rs:10:17 + | +LL | type Foo = impl Sized; + | ---------- the found opaque type +... +LL | let _: [u8; (N / 2) as Foo] = [0; (N / 2) as usize]; + | ^^^^^^^^^^^^^^ expected `usize`, found opaque type + | + = note: expected type `usize` + found opaque type `Foo` + +error[E0605]: non-primitive cast: `usize` as `Foo` + --> $DIR/opaque_type.rs:10:17 + | +LL | let _: [u8; (N / 2) as Foo] = [0; (N / 2) as usize]; + | ^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0308, E0605. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/opaque_types.rs b/tests/ui/const-generics/opaque_types.rs new file mode 100644 index 0000000000000..ccf70f4fb37b0 --- /dev/null +++ b/tests/ui/const-generics/opaque_types.rs @@ -0,0 +1,13 @@ +#![feature(type_alias_impl_trait)] + +type Foo = impl Sized; +//~^ ERROR: cycle +//~| ERROR: cycle + +fn foo() {} +//~^ ERROR: `Foo` is forbidden as the type of a const generic parameter + +fn main() { + foo::<42>(); + //~^ ERROR: mismatched types +} diff --git a/tests/ui/const-generics/opaque_types.stderr b/tests/ui/const-generics/opaque_types.stderr new file mode 100644 index 0000000000000..f03bca69a8bbf --- /dev/null +++ b/tests/ui/const-generics/opaque_types.stderr @@ -0,0 +1,125 @@ +error[E0308]: mismatched types + --> $DIR/opaque_types.rs:11:11 + | +LL | type Foo = impl Sized; + | ---------- the expected opaque type +... +LL | foo::<42>(); + | ^^ expected opaque type, found integer + | + = note: expected opaque type `Foo` + found type `{integer}` + +error[E0391]: cycle detected when computing type of `Foo::{opaque#0}` + --> $DIR/opaque_types.rs:3:12 + | +LL | type Foo = impl Sized; + | ^^^^^^^^^^ + | +note: ...which requires computing type of opaque `Foo::{opaque#0}`... + --> $DIR/opaque_types.rs:3:12 + | +LL | type Foo = impl Sized; + | ^^^^^^^^^^ +note: ...which requires type-checking `main`... + --> $DIR/opaque_types.rs:10:1 + | +LL | fn main() { + | ^^^^^^^^^ +note: ...which requires evaluating type-level constant... + --> $DIR/opaque_types.rs:11:11 + | +LL | foo::<42>(); + | ^^ +note: ...which requires const-evaluating + checking `main::{constant#0}`... + --> $DIR/opaque_types.rs:11:11 + | +LL | foo::<42>(); + | ^^ +note: ...which requires caching mir of `main::{constant#0}` for CTFE... + --> $DIR/opaque_types.rs:11:11 + | +LL | foo::<42>(); + | ^^ +note: ...which requires elaborating drops for `main::{constant#0}`... + --> $DIR/opaque_types.rs:11:11 + | +LL | foo::<42>(); + | ^^ + = note: ...which requires normalizing `Foo`... + = note: ...which again requires computing type of `Foo::{opaque#0}`, completing the cycle +note: cycle used when checking that `Foo::{opaque#0}` is well-formed + --> $DIR/opaque_types.rs:3:12 + | +LL | type Foo = impl Sized; + | ^^^^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error: `Foo` is forbidden as the type of a const generic parameter + --> $DIR/opaque_types.rs:7:17 + | +LL | fn foo() {} + | ^^^ + | + = note: the only supported types are integers, `bool` and `char` + +error[E0391]: cycle detected when computing type of opaque `Foo::{opaque#0}` + --> $DIR/opaque_types.rs:3:12 + | +LL | type Foo = impl Sized; + | ^^^^^^^^^^ + | +note: ...which requires type-checking `main`... + --> $DIR/opaque_types.rs:10:1 + | +LL | fn main() { + | ^^^^^^^^^ +note: ...which requires evaluating type-level constant... + --> $DIR/opaque_types.rs:11:11 + | +LL | foo::<42>(); + | ^^ +note: ...which requires const-evaluating + checking `main::{constant#0}`... + --> $DIR/opaque_types.rs:11:11 + | +LL | foo::<42>(); + | ^^ +note: ...which requires caching mir of `main::{constant#0}` for CTFE... + --> $DIR/opaque_types.rs:11:11 + | +LL | foo::<42>(); + | ^^ +note: ...which requires elaborating drops for `main::{constant#0}`... + --> $DIR/opaque_types.rs:11:11 + | +LL | foo::<42>(); + | ^^ +note: ...which requires borrow-checking `main::{constant#0}`... + --> $DIR/opaque_types.rs:11:11 + | +LL | foo::<42>(); + | ^^ +note: ...which requires promoting constants in MIR for `main::{constant#0}`... + --> $DIR/opaque_types.rs:11:11 + | +LL | foo::<42>(); + | ^^ +note: ...which requires const checking `main::{constant#0}`... + --> $DIR/opaque_types.rs:11:11 + | +LL | foo::<42>(); + | ^^ + = note: ...which requires computing whether `Foo` is freeze... + = note: ...which requires evaluating trait selection obligation `Foo: core::marker::Freeze`... + = note: ...which again requires computing type of opaque `Foo::{opaque#0}`, completing the cycle +note: cycle used when computing type of `Foo::{opaque#0}` + --> $DIR/opaque_types.rs:3:12 + | +LL | type Foo = impl Sized; + | ^^^^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0308, E0391. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/opaque_types2.rs b/tests/ui/const-generics/opaque_types2.rs new file mode 100644 index 0000000000000..fd57438bb6171 --- /dev/null +++ b/tests/ui/const-generics/opaque_types2.rs @@ -0,0 +1,17 @@ +#![feature(type_alias_impl_trait)] + +type Foo = impl Sized; + +fn foo() {} + +const C: Foo = 42; + +fn bar() +where + Foo:, +{ + foo::(); + //~^ ERROR: mismatched types +} + +fn main() {} diff --git a/tests/ui/const-generics/opaque_types2.stderr b/tests/ui/const-generics/opaque_types2.stderr new file mode 100644 index 0000000000000..2fb1669b7bfab --- /dev/null +++ b/tests/ui/const-generics/opaque_types2.stderr @@ -0,0 +1,15 @@ +error[E0308]: mismatched types + --> $DIR/opaque_types2.rs:13:11 + | +LL | type Foo = impl Sized; + | ---------- the found opaque type +... +LL | foo::(); + | ^ expected `u32`, found opaque type + | + = note: expected type `u32` + found opaque type `Foo` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/consts/const-eval/parse_ints.rs b/tests/ui/consts/const-eval/parse_ints.rs new file mode 100644 index 0000000000000..ff9fc47e65c33 --- /dev/null +++ b/tests/ui/consts/const-eval/parse_ints.rs @@ -0,0 +1,10 @@ +#![feature(const_int_from_str)] + +const _OK: () = match i32::from_str_radix("-1234", 10) { + Ok(x) => assert!(x == -1234), + Err(_) => panic!(), +}; +const _TOO_LOW: () = { u64::from_str_radix("12345ABCD", 1); }; +const _TOO_HIGH: () = { u64::from_str_radix("12345ABCD", 37); }; + +fn main () {} diff --git a/tests/ui/consts/const-eval/parse_ints.stderr b/tests/ui/consts/const-eval/parse_ints.stderr new file mode 100644 index 0000000000000..9e49fe433a126 --- /dev/null +++ b/tests/ui/consts/const-eval/parse_ints.stderr @@ -0,0 +1,31 @@ +error[E0080]: evaluation of constant value failed + --> $SRC_DIR/core/src/num/mod.rs:LL:COL + | + = note: the evaluated program panicked at 'from_str_radix_int: must lie in the range `[2, 36]`', $SRC_DIR/core/src/num/mod.rs:LL:COL + | +note: inside `core::num::::from_str_radix` + --> $SRC_DIR/core/src/num/mod.rs:LL:COL +note: inside `_TOO_LOW` + --> $DIR/parse_ints.rs:7:24 + | +LL | const _TOO_LOW: () = { u64::from_str_radix("12345ABCD", 1); }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the macro `from_str_radix` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0080]: evaluation of constant value failed + --> $SRC_DIR/core/src/num/mod.rs:LL:COL + | + = note: the evaluated program panicked at 'from_str_radix_int: must lie in the range `[2, 36]`', $SRC_DIR/core/src/num/mod.rs:LL:COL + | +note: inside `core::num::::from_str_radix` + --> $SRC_DIR/core/src/num/mod.rs:LL:COL +note: inside `_TOO_HIGH` + --> $DIR/parse_ints.rs:8:25 + | +LL | const _TOO_HIGH: () = { u64::from_str_radix("12345ABCD", 37); }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the macro `from_str_radix` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.mir.stderr b/tests/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.mir.stderr deleted file mode 100644 index 34ec8aadbcf3b..0000000000000 --- a/tests/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.mir.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0133]: call to unsafe function is unsafe and requires unsafe function or block - --> $DIR/const-extern-fn-requires-unsafe.rs:12:5 - | -LL | foo(); - | ^^^^^ call to unsafe function - | - = note: consult the function's documentation for information on how to avoid undefined behavior - -error[E0133]: call to unsafe function is unsafe and requires unsafe function or block - --> $DIR/const-extern-fn-requires-unsafe.rs:9:17 - | -LL | let a: [u8; foo()]; - | ^^^^^ call to unsafe function - | - = note: consult the function's documentation for information on how to avoid undefined behavior - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.thir.stderr b/tests/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.thir.stderr deleted file mode 100644 index e6b8173eb051a..0000000000000 --- a/tests/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.thir.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0133]: call to unsafe function `foo` is unsafe and requires unsafe function or block - --> $DIR/const-extern-fn-requires-unsafe.rs:12:5 - | -LL | foo(); - | ^^^^^ call to unsafe function - | - = note: consult the function's documentation for information on how to avoid undefined behavior - -error[E0133]: call to unsafe function `foo` is unsafe and requires unsafe function or block - --> $DIR/const-extern-fn-requires-unsafe.rs:9:17 - | -LL | let a: [u8; foo()]; - | ^^^^^ call to unsafe function - | - = note: consult the function's documentation for information on how to avoid undefined behavior - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/consts/const-int-unchecked.rs b/tests/ui/consts/const-int-unchecked.rs index 902a668488b87..8de28aa2bb171 100644 --- a/tests/ui/consts/const-int-unchecked.rs +++ b/tests/ui/consts/const-int-unchecked.rs @@ -1,5 +1,5 @@ #![feature(core_intrinsics)] -#![feature(const_int_unchecked_arith)] + use std::intrinsics; @@ -27,7 +27,7 @@ const SHL_U128: u128 = unsafe { intrinsics::unchecked_shl(5_u128, 128) }; const SHL_I8: i8 = unsafe { intrinsics::unchecked_shl(5_i8, 8) }; //~^ ERROR evaluation of constant value failed -const SHL_I16: i16 = unsafe { intrinsics::unchecked_shl(5_16, 16) }; +const SHL_I16: i16 = unsafe { intrinsics::unchecked_shl(5_i16, 16) }; //~^ ERROR evaluation of constant value failed const SHL_I32: i32 = unsafe { intrinsics::unchecked_shl(5_i32, 32) }; //~^ ERROR evaluation of constant value failed @@ -40,7 +40,7 @@ const SHL_I128: i128 = unsafe { intrinsics::unchecked_shl(5_i128, 128) }; const SHL_I8_NEG: i8 = unsafe { intrinsics::unchecked_shl(5_i8, -1) }; //~^ ERROR evaluation of constant value failed -const SHL_I16_NEG: i16 = unsafe { intrinsics::unchecked_shl(5_16, -1) }; +const SHL_I16_NEG: i16 = unsafe { intrinsics::unchecked_shl(5_i16, -1) }; //~^ ERROR evaluation of constant value failed const SHL_I32_NEG: i32 = unsafe { intrinsics::unchecked_shl(5_i32, -1) }; //~^ ERROR evaluation of constant value failed @@ -54,7 +54,7 @@ const SHL_I128_NEG: i128 = unsafe { intrinsics::unchecked_shl(5_i128, -1) }; const SHL_I8_NEG_RANDOM: i8 = unsafe { intrinsics::unchecked_shl(5_i8, -6) }; //~^ ERROR evaluation of constant value failed -const SHL_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shl(5_16, -13) }; +const SHL_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shl(5_i16, -13) }; //~^ ERROR evaluation of constant value failed const SHL_I32_NEG_RANDOM: i32 = unsafe { intrinsics::unchecked_shl(5_i32, -25) }; //~^ ERROR evaluation of constant value failed @@ -82,7 +82,7 @@ const SHR_U128: u128 = unsafe { intrinsics::unchecked_shr(5_u128, 128) }; const SHR_I8: i8 = unsafe { intrinsics::unchecked_shr(5_i8, 8) }; //~^ ERROR evaluation of constant value failed -const SHR_I16: i16 = unsafe { intrinsics::unchecked_shr(5_16, 16) }; +const SHR_I16: i16 = unsafe { intrinsics::unchecked_shr(5_i16, 16) }; //~^ ERROR evaluation of constant value failed const SHR_I32: i32 = unsafe { intrinsics::unchecked_shr(5_i32, 32) }; //~^ ERROR evaluation of constant value failed @@ -95,7 +95,7 @@ const SHR_I128: i128 = unsafe { intrinsics::unchecked_shr(5_i128, 128) }; const SHR_I8_NEG: i8 = unsafe { intrinsics::unchecked_shr(5_i8, -1) }; //~^ ERROR evaluation of constant value failed -const SHR_I16_NEG: i16 = unsafe { intrinsics::unchecked_shr(5_16, -1) }; +const SHR_I16_NEG: i16 = unsafe { intrinsics::unchecked_shr(5_i16, -1) }; //~^ ERROR evaluation of constant value failed const SHR_I32_NEG: i32 = unsafe { intrinsics::unchecked_shr(5_i32, -1) }; //~^ ERROR evaluation of constant value failed @@ -109,7 +109,7 @@ const SHR_I128_NEG: i128 = unsafe { intrinsics::unchecked_shr(5_i128, -1) }; const SHR_I8_NEG_RANDOM: i8 = unsafe { intrinsics::unchecked_shr(5_i8, -6) }; //~^ ERROR evaluation of constant value failed -const SHR_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shr(5_16, -13) }; +const SHR_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shr(5_i16, -13) }; //~^ ERROR evaluation of constant value failed const SHR_I32_NEG_RANDOM: i32 = unsafe { intrinsics::unchecked_shr(5_i32, -25) }; //~^ ERROR evaluation of constant value failed diff --git a/tests/ui/consts/const-int-unchecked.stderr b/tests/ui/consts/const-int-unchecked.stderr index ad14c8f68f8cb..84b222972a13c 100644 --- a/tests/ui/consts/const-int-unchecked.stderr +++ b/tests/ui/consts/const-int-unchecked.stderr @@ -37,8 +37,8 @@ LL | const SHL_I8: i8 = unsafe { intrinsics::unchecked_shl(5_i8, 8) }; error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:30:31 | -LL | const SHL_I16: i16 = unsafe { intrinsics::unchecked_shl(5_16, 16) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 16 in `unchecked_shl` +LL | const SHL_I16: i16 = unsafe { intrinsics::unchecked_shl(5_i16, 16) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 16 in `unchecked_shl` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:32:31 @@ -67,8 +67,8 @@ LL | const SHL_I8_NEG: i8 = unsafe { intrinsics::unchecked_shl(5_i8, -1) }; error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:43:35 | -LL | const SHL_I16_NEG: i16 = unsafe { intrinsics::unchecked_shl(5_16, -1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -1 in `unchecked_shl` +LL | const SHL_I16_NEG: i16 = unsafe { intrinsics::unchecked_shl(5_i16, -1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -1 in `unchecked_shl` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:45:35 @@ -97,8 +97,8 @@ LL | const SHL_I8_NEG_RANDOM: i8 = unsafe { intrinsics::unchecked_shl(5_i8, -6) error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:57:42 | -LL | const SHL_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shl(5_16, -13) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -13 in `unchecked_shl` +LL | const SHL_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shl(5_i16, -13) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -13 in `unchecked_shl` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:59:42 @@ -157,8 +157,8 @@ LL | const SHR_I8: i8 = unsafe { intrinsics::unchecked_shr(5_i8, 8) }; error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:85:31 | -LL | const SHR_I16: i16 = unsafe { intrinsics::unchecked_shr(5_16, 16) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 16 in `unchecked_shr` +LL | const SHR_I16: i16 = unsafe { intrinsics::unchecked_shr(5_i16, 16) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 16 in `unchecked_shr` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:87:31 @@ -187,8 +187,8 @@ LL | const SHR_I8_NEG: i8 = unsafe { intrinsics::unchecked_shr(5_i8, -1) }; error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:98:35 | -LL | const SHR_I16_NEG: i16 = unsafe { intrinsics::unchecked_shr(5_16, -1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -1 in `unchecked_shr` +LL | const SHR_I16_NEG: i16 = unsafe { intrinsics::unchecked_shr(5_i16, -1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -1 in `unchecked_shr` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:100:35 @@ -217,8 +217,8 @@ LL | const SHR_I8_NEG_RANDOM: i8 = unsafe { intrinsics::unchecked_shr(5_i8, -6) error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:112:42 | -LL | const SHR_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shr(5_16, -13) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -13 in `unchecked_shr` +LL | const SHR_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shr(5_i16, -13) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -13 in `unchecked_shr` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:114:42 diff --git a/tests/ui/derives/auxiliary/rustc-serialize.rs b/tests/ui/derives/auxiliary/rustc-serialize.rs new file mode 100644 index 0000000000000..24177af931c4a --- /dev/null +++ b/tests/ui/derives/auxiliary/rustc-serialize.rs @@ -0,0 +1,16 @@ +#![crate_type = "lib"] + +pub trait Decoder { + type Error; + + fn read_enum(&mut self, name: &str, f: F) -> Result + where F: FnOnce(&mut Self) -> Result; + fn read_enum_variant(&mut self, names: &[&str], f: F) + -> Result + where F: FnMut(&mut Self, usize) -> Result; + +} + +pub trait Decodable: Sized { + fn decode(d: &mut D) -> Result; +} diff --git a/tests/ui/derives/rustc-decodable-issue-123156.rs b/tests/ui/derives/rustc-decodable-issue-123156.rs new file mode 100644 index 0000000000000..1983837ed8d46 --- /dev/null +++ b/tests/ui/derives/rustc-decodable-issue-123156.rs @@ -0,0 +1,11 @@ +//@ check-pass +//@ edition:2021 +//@ aux-build:rustc-serialize.rs + +#![crate_type = "lib"] +#![allow(deprecated, soft_unstable)] + +extern crate rustc_serialize; + +#[derive(RustcDecodable)] +pub enum Foo {} diff --git a/tests/ui/derives/rustc-decodable-issue-123156.stderr b/tests/ui/derives/rustc-decodable-issue-123156.stderr new file mode 100644 index 0000000000000..ee7b33d59bb9b --- /dev/null +++ b/tests/ui/derives/rustc-decodable-issue-123156.stderr @@ -0,0 +1,10 @@ +Future incompatibility report: Future breakage diagnostic: +warning: use of unstable library feature 'rustc_encodable_decodable': derive macro for `rustc-serialize`; should not be used in new code + --> $DIR/rustc-decodable-issue-123156.rs:10:10 + | +LL | #[derive(RustcDecodable)] + | ^^^^^^^^^^^^^^ + | + = warning: 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 #64266 + diff --git a/tests/ui/does-nothing.rs b/tests/ui/does-nothing.rs deleted file mode 100644 index e4992e2cfd3c9..0000000000000 --- a/tests/ui/does-nothing.rs +++ /dev/null @@ -1,2 +0,0 @@ -fn main() { println!("doing"); this_does_nothing_what_the; println!("boing"); } -//~^ ERROR cannot find value `this_does_nothing_what_the` in this scope diff --git a/tests/ui/does-nothing.stderr b/tests/ui/does-nothing.stderr deleted file mode 100644 index d5ea3626e81c8..0000000000000 --- a/tests/ui/does-nothing.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0425]: cannot find value `this_does_nothing_what_the` in this scope - --> $DIR/does-nothing.rs:1:32 - | -LL | fn main() { println!("doing"); this_does_nothing_what_the; println!("boing"); } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/extern/issue-95829.stderr b/tests/ui/extern/issue-95829.stderr index b902f0ef8f5c0..16504d1f0c9d0 100644 --- a/tests/ui/extern/issue-95829.stderr +++ b/tests/ui/extern/issue-95829.stderr @@ -16,17 +16,12 @@ LL | | } = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html error: functions in `extern` blocks cannot have qualifiers - --> $DIR/issue-95829.rs:4:14 + --> $DIR/issue-95829.rs:4:5 | LL | extern { | ------ in this `extern` block LL | async fn L() { - | ^ - | -help: remove the qualifiers - | -LL | fn L() { - | ~~ + | ^^^^^ help: remove this qualifier error: aborting due to 2 previous errors diff --git a/tests/ui/feature-gates/feature-gate-f128.stderr b/tests/ui/feature-gates/feature-gate-f128.e2015.stderr similarity index 91% rename from tests/ui/feature-gates/feature-gate-f128.stderr rename to tests/ui/feature-gates/feature-gate-f128.e2015.stderr index 299375c9aed90..771aee79dce16 100644 --- a/tests/ui/feature-gates/feature-gate-f128.stderr +++ b/tests/ui/feature-gates/feature-gate-f128.e2015.stderr @@ -1,5 +1,5 @@ error[E0658]: the type `f128` is unstable - --> $DIR/feature-gate-f128.rs:3:10 + --> $DIR/feature-gate-f128.rs:7:10 | LL | const A: f128 = 10.0; | ^^^^ @@ -9,7 +9,7 @@ LL | const A: f128 = 10.0; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: the type `f128` is unstable - --> $DIR/feature-gate-f128.rs:6:12 + --> $DIR/feature-gate-f128.rs:10:12 | LL | let a: f128 = 100.0; | ^^^^ @@ -19,7 +19,7 @@ LL | let a: f128 = 100.0; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: the type `f128` is unstable - --> $DIR/feature-gate-f128.rs:11:11 + --> $DIR/feature-gate-f128.rs:15:11 | LL | fn foo(a: f128) {} | ^^^^ @@ -29,7 +29,7 @@ LL | fn foo(a: f128) {} = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: the type `f128` is unstable - --> $DIR/feature-gate-f128.rs:14:8 + --> $DIR/feature-gate-f128.rs:18:8 | LL | a: f128, | ^^^^ @@ -39,7 +39,7 @@ LL | a: f128, = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: the type `f128` is unstable - --> $DIR/feature-gate-f128.rs:7:13 + --> $DIR/feature-gate-f128.rs:11:13 | LL | let b = 0.0f128; | ^^^^^^^ diff --git a/tests/ui/feature-gates/feature-gate-f128.e2018.stderr b/tests/ui/feature-gates/feature-gate-f128.e2018.stderr new file mode 100644 index 0000000000000..771aee79dce16 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-f128.e2018.stderr @@ -0,0 +1,53 @@ +error[E0658]: the type `f128` is unstable + --> $DIR/feature-gate-f128.rs:7:10 + | +LL | const A: f128 = 10.0; + | ^^^^ + | + = note: see issue #116909 for more information + = help: add `#![feature(f128)]` 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 type `f128` is unstable + --> $DIR/feature-gate-f128.rs:10:12 + | +LL | let a: f128 = 100.0; + | ^^^^ + | + = note: see issue #116909 for more information + = help: add `#![feature(f128)]` 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 type `f128` is unstable + --> $DIR/feature-gate-f128.rs:15:11 + | +LL | fn foo(a: f128) {} + | ^^^^ + | + = note: see issue #116909 for more information + = help: add `#![feature(f128)]` 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 type `f128` is unstable + --> $DIR/feature-gate-f128.rs:18:8 + | +LL | a: f128, + | ^^^^ + | + = note: see issue #116909 for more information + = help: add `#![feature(f128)]` 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 type `f128` is unstable + --> $DIR/feature-gate-f128.rs:11:13 + | +LL | let b = 0.0f128; + | ^^^^^^^ + | + = note: see issue #116909 for more information + = help: add `#![feature(f128)]` 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 5 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-f128.rs b/tests/ui/feature-gates/feature-gate-f128.rs index 7f60fb6afa080..d25b6dde4ee47 100644 --- a/tests/ui/feature-gates/feature-gate-f128.rs +++ b/tests/ui/feature-gates/feature-gate-f128.rs @@ -1,3 +1,7 @@ +//@ revisions: e2015 e2018 +// +//@[e2018] edition:2018 + #![allow(unused)] const A: f128 = 10.0; //~ ERROR the type `f128` is unstable diff --git a/tests/ui/feature-gates/feature-gate-f16.stderr b/tests/ui/feature-gates/feature-gate-f16.e2015.stderr similarity index 91% rename from tests/ui/feature-gates/feature-gate-f16.stderr rename to tests/ui/feature-gates/feature-gate-f16.e2015.stderr index e54b54a47bde6..2bb3b59465a08 100644 --- a/tests/ui/feature-gates/feature-gate-f16.stderr +++ b/tests/ui/feature-gates/feature-gate-f16.e2015.stderr @@ -1,5 +1,5 @@ error[E0658]: the type `f16` is unstable - --> $DIR/feature-gate-f16.rs:3:10 + --> $DIR/feature-gate-f16.rs:7:10 | LL | const A: f16 = 10.0; | ^^^ @@ -9,7 +9,7 @@ LL | const A: f16 = 10.0; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: the type `f16` is unstable - --> $DIR/feature-gate-f16.rs:6:12 + --> $DIR/feature-gate-f16.rs:10:12 | LL | let a: f16 = 100.0; | ^^^ @@ -19,7 +19,7 @@ LL | let a: f16 = 100.0; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: the type `f16` is unstable - --> $DIR/feature-gate-f16.rs:11:11 + --> $DIR/feature-gate-f16.rs:15:11 | LL | fn foo(a: f16) {} | ^^^ @@ -29,7 +29,7 @@ LL | fn foo(a: f16) {} = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: the type `f16` is unstable - --> $DIR/feature-gate-f16.rs:14:8 + --> $DIR/feature-gate-f16.rs:18:8 | LL | a: f16, | ^^^ @@ -39,7 +39,7 @@ LL | a: f16, = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: the type `f16` is unstable - --> $DIR/feature-gate-f16.rs:7:13 + --> $DIR/feature-gate-f16.rs:11:13 | LL | let b = 0.0f16; | ^^^^^^ diff --git a/tests/ui/feature-gates/feature-gate-f16.e2018.stderr b/tests/ui/feature-gates/feature-gate-f16.e2018.stderr new file mode 100644 index 0000000000000..2bb3b59465a08 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-f16.e2018.stderr @@ -0,0 +1,53 @@ +error[E0658]: the type `f16` is unstable + --> $DIR/feature-gate-f16.rs:7:10 + | +LL | const A: f16 = 10.0; + | ^^^ + | + = note: see issue #116909 for more information + = help: add `#![feature(f16)]` 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 type `f16` is unstable + --> $DIR/feature-gate-f16.rs:10:12 + | +LL | let a: f16 = 100.0; + | ^^^ + | + = note: see issue #116909 for more information + = help: add `#![feature(f16)]` 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 type `f16` is unstable + --> $DIR/feature-gate-f16.rs:15:11 + | +LL | fn foo(a: f16) {} + | ^^^ + | + = note: see issue #116909 for more information + = help: add `#![feature(f16)]` 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 type `f16` is unstable + --> $DIR/feature-gate-f16.rs:18:8 + | +LL | a: f16, + | ^^^ + | + = note: see issue #116909 for more information + = help: add `#![feature(f16)]` 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 type `f16` is unstable + --> $DIR/feature-gate-f16.rs:11:13 + | +LL | let b = 0.0f16; + | ^^^^^^ + | + = note: see issue #116909 for more information + = help: add `#![feature(f16)]` 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 5 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-f16.rs b/tests/ui/feature-gates/feature-gate-f16.rs index 31d8f87f3ba6d..af906d71f5fe2 100644 --- a/tests/ui/feature-gates/feature-gate-f16.rs +++ b/tests/ui/feature-gates/feature-gate-f16.rs @@ -1,3 +1,7 @@ +//@ revisions: e2015 e2018 +// +//@[e2018] edition:2018 + #![allow(unused)] const A: f16 = 10.0; //~ ERROR the type `f16` is unstable diff --git a/tests/ui/feature-gates/feature-gate-mut-ref.rs b/tests/ui/feature-gates/feature-gate-mut-ref.rs new file mode 100644 index 0000000000000..806b25de66ff2 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-mut-ref.rs @@ -0,0 +1,13 @@ +fn main() { + let mut ref x = 10; //~ ERROR [E0658] + x = &11; + let ref mut y = 12; + *y = 13; + let mut ref mut z = 14; //~ ERROR [E0658] + z = &mut 15; + + #[cfg(FALSE)] + let mut ref x = 10; //~ ERROR [E0658] + #[cfg(FALSE)] + let mut ref mut y = 10; //~ ERROR [E0658] +} diff --git a/tests/ui/feature-gates/feature-gate-mut-ref.stderr b/tests/ui/feature-gates/feature-gate-mut-ref.stderr new file mode 100644 index 0000000000000..d3eb674e92dec --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-mut-ref.stderr @@ -0,0 +1,43 @@ +error[E0658]: mutable by-reference bindings are experimental + --> $DIR/feature-gate-mut-ref.rs:2:17 + | +LL | let mut ref x = 10; + | ^ + | + = note: see issue #123076 for more information + = help: add `#![feature(mut_ref)]` 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]: mutable by-reference bindings are experimental + --> $DIR/feature-gate-mut-ref.rs:6:21 + | +LL | let mut ref mut z = 14; + | ^ + | + = note: see issue #123076 for more information + = help: add `#![feature(mut_ref)]` 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]: mutable by-reference bindings are experimental + --> $DIR/feature-gate-mut-ref.rs:10:17 + | +LL | let mut ref x = 10; + | ^ + | + = note: see issue #123076 for more information + = help: add `#![feature(mut_ref)]` 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]: mutable by-reference bindings are experimental + --> $DIR/feature-gate-mut-ref.rs:12:21 + | +LL | let mut ref mut y = 10; + | ^ + | + = note: see issue #123076 for more information + = help: add `#![feature(mut_ref)]` 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 4 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-proc_macro_byte_character.rs b/tests/ui/feature-gates/feature-gate-proc_macro_byte_character.rs deleted file mode 100644 index 03071c351a44d..0000000000000 --- a/tests/ui/feature-gates/feature-gate-proc_macro_byte_character.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ force-host -#![crate_type = "proc-macro"] - -extern crate proc_macro; - -use proc_macro::Literal; - -fn test() { - Literal::byte_character(b'a'); //~ ERROR use of unstable library feature 'proc_macro_byte_character' -} diff --git a/tests/ui/feature-gates/feature-gate-proc_macro_byte_character.stderr b/tests/ui/feature-gates/feature-gate-proc_macro_byte_character.stderr deleted file mode 100644 index c14d19381c8bc..0000000000000 --- a/tests/ui/feature-gates/feature-gate-proc_macro_byte_character.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0658]: use of unstable library feature 'proc_macro_byte_character' - --> $DIR/feature-gate-proc_macro_byte_character.rs:9:5 - | -LL | Literal::byte_character(b'a'); - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #115268 for more information - = help: add `#![feature(proc_macro_byte_character)]` 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-proc_macro_c_str_literals.rs b/tests/ui/feature-gates/feature-gate-proc_macro_c_str_literals.rs deleted file mode 100644 index 1750fe952f560..0000000000000 --- a/tests/ui/feature-gates/feature-gate-proc_macro_c_str_literals.rs +++ /dev/null @@ -1,11 +0,0 @@ -//@ edition: 2021 -//@ force-host -#![crate_type = "proc-macro"] - -extern crate proc_macro; - -use proc_macro::Literal; - -fn test() { - Literal::c_string(c"a"); //~ ERROR use of unstable library feature 'proc_macro_c_str_literals' -} diff --git a/tests/ui/feature-gates/feature-gate-proc_macro_c_str_literals.stderr b/tests/ui/feature-gates/feature-gate-proc_macro_c_str_literals.stderr deleted file mode 100644 index 9bba1d50ce362..0000000000000 --- a/tests/ui/feature-gates/feature-gate-proc_macro_c_str_literals.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0658]: use of unstable library feature 'proc_macro_c_str_literals' - --> $DIR/feature-gate-proc_macro_c_str_literals.rs:10:5 - | -LL | Literal::c_string(c"a"); - | ^^^^^^^^^^^^^^^^^ - | - = note: see issue #119750 for more information - = help: add `#![feature(proc_macro_c_str_literals)]` 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-ref_pat_everywhere.rs b/tests/ui/feature-gates/feature-gate-ref_pat_everywhere.rs new file mode 100644 index 0000000000000..ed5db56e0e83d --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-ref_pat_everywhere.rs @@ -0,0 +1,14 @@ +pub fn main() { + if let Some(Some(&x)) = &Some(&Some(0)) { + //~^ ERROR: mismatched types [E0308] + let _: u32 = x; + } + if let Some(&Some(x)) = &Some(Some(0)) { + //~^ ERROR: mismatched types [E0308] + let _: u32 = x; + } + if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { + //~^ ERROR: mismatched types [E0308] + let _: u32 = x; + } +} diff --git a/tests/ui/feature-gates/feature-gate-ref_pat_everywhere.stderr b/tests/ui/feature-gates/feature-gate-ref_pat_everywhere.stderr new file mode 100644 index 0000000000000..0f0051325cdf0 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-ref_pat_everywhere.stderr @@ -0,0 +1,49 @@ +error[E0308]: mismatched types + --> $DIR/feature-gate-ref_pat_everywhere.rs:2:22 + | +LL | if let Some(Some(&x)) = &Some(&Some(0)) { + | ^^ --------------- this expression has type `&Option<&Option<{integer}>>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL | if let Some(Some(x)) = &Some(&Some(0)) { + | ~ + +error[E0308]: mismatched types + --> $DIR/feature-gate-ref_pat_everywhere.rs:6:17 + | +LL | if let Some(&Some(x)) = &Some(Some(0)) { + | ^^^^^^^^ -------------- this expression has type `&Option>` + | | + | expected `Option<{integer}>`, found `&_` + | + = note: expected enum `Option<{integer}>` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/feature-gate-ref_pat_everywhere.rs:10:22 + | +LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { + | ^^^^^^ ----------------------- this expression has type `&mut Option<&mut Option<{integer}>>` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/feature-gate-ref_pat_everywhere.rs:10:22 + | +LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { + | ^^^^^^ +help: consider removing `&mut` from the pattern + | +LL | if let Some(Some(x)) = &mut Some(&mut Some(0)) { + | ~ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/fn/fn-item-lifetime-bounds.rs b/tests/ui/fn/fn-item-lifetime-bounds.rs deleted file mode 100644 index b80b7eade23d1..0000000000000 --- a/tests/ui/fn/fn-item-lifetime-bounds.rs +++ /dev/null @@ -1,37 +0,0 @@ -//@ check-pass -//@ known-bug: #84533 - -// Should fail. Lifetimes are checked correctly when `foo` is called, but NOT -// when only the lifetime parameters are instantiated. - -use std::marker::PhantomData; - -#[allow(dead_code)] -fn foo<'b, 'a>() -> PhantomData<&'b &'a ()> { - PhantomData -} - -#[allow(dead_code)] -#[allow(path_statements)] -fn caller<'b, 'a>() { - foo::<'b, 'a>; -} - -// In contrast to above, below code correctly does NOT compile. -// fn caller<'b, 'a>() { -// foo::<'b, 'a>(); -// } - -// error: lifetime may not live long enough -// --> src/main.rs:22:5 -// | -// 21 | fn caller<'b, 'a>() { -// | -- -- lifetime `'a` defined here -// | | -// | lifetime `'b` defined here -// 22 | foo::<'b, 'a>(); -// | ^^^^^^^^^^^^^^^ requires that `'a` must outlive `'b` -// | -// = help: consider adding the following bound: `'a: 'b` - -fn main() {} diff --git a/tests/ui/fn/fn-item-type.stderr b/tests/ui/fn/fn-item-type.stderr index da90b8b81c855..76cdbcceac841 100644 --- a/tests/ui/fn/fn-item-type.stderr +++ b/tests/ui/fn/fn-item-type.stderr @@ -2,8 +2,9 @@ error[E0308]: mismatched types --> $DIR/fn-item-type.rs:22:19 | LL | eq(foo::, bar::); - | -- ^^^^^^^^^ expected fn item, found a different fn item - | | + | -- --------- ^^^^^^^^^ expected fn item, found a different fn item + | | | + | | expected all arguments to be this fn item type because they need to match the type of this parameter | arguments to this function are incorrect | = note: expected fn item `fn(_) -> _ {foo::}` @@ -13,15 +14,19 @@ note: function defined here --> $DIR/fn-item-type.rs:11:4 | LL | fn eq(x: T, y: T) {} - | ^^ ---- + | ^^ - ---- ---- this parameter needs to match the fn item type of `x` + | | | + | | `y` needs to match the fn item type of this parameter + | `x` and `y` all reference this parameter T = help: consider casting both fn items to fn pointers using `as fn(isize) -> isize` error[E0308]: mismatched types --> $DIR/fn-item-type.rs:29:19 | LL | eq(foo::, foo::); - | -- ^^^^^^^^^ expected `u8`, found `i8` - | | + | -- --------- ^^^^^^^^^ expected `u8`, found `i8` + | | | + | | expected all arguments to be this fn item type because they need to match the type of this parameter | arguments to this function are incorrect | = note: expected fn item `fn(_) -> _ {foo::}` @@ -31,15 +36,19 @@ note: function defined here --> $DIR/fn-item-type.rs:11:4 | LL | fn eq(x: T, y: T) {} - | ^^ ---- + | ^^ - ---- ---- this parameter needs to match the fn item type of `x` + | | | + | | `y` needs to match the fn item type of this parameter + | `x` and `y` all reference this parameter T = help: consider casting both fn items to fn pointers using `as fn(isize) -> isize` error[E0308]: mismatched types --> $DIR/fn-item-type.rs:34:23 | LL | eq(bar::, bar::>); - | -- ^^^^^^^^^^^^^^ expected `String`, found `Vec` - | | + | -- ------------- ^^^^^^^^^^^^^^ expected `String`, found `Vec` + | | | + | | expected all arguments to be this fn item type because they need to match the type of this parameter | arguments to this function are incorrect | = note: expected fn item `fn(_) -> _ {bar::}` @@ -49,15 +58,19 @@ note: function defined here --> $DIR/fn-item-type.rs:11:4 | LL | fn eq(x: T, y: T) {} - | ^^ ---- + | ^^ - ---- ---- this parameter needs to match the fn item type of `x` + | | | + | | `y` needs to match the fn item type of this parameter + | `x` and `y` all reference this parameter T = help: consider casting both fn items to fn pointers using `as fn(isize) -> isize` error[E0308]: mismatched types --> $DIR/fn-item-type.rs:40:26 | LL | eq(::foo, ::foo); - | -- ^^^^^^^^^^^^^^^^^ expected `u8`, found `u16` - | | + | -- ---------------- ^^^^^^^^^^^^^^^^^ expected `u8`, found `u16` + | | | + | | expected all arguments to be this fn item type because they need to match the type of this parameter | arguments to this function are incorrect | = note: expected fn item `fn() {::foo}` @@ -67,15 +80,19 @@ note: function defined here --> $DIR/fn-item-type.rs:11:4 | LL | fn eq(x: T, y: T) {} - | ^^ ---- + | ^^ - ---- ---- this parameter needs to match the fn item type of `x` + | | | + | | `y` needs to match the fn item type of this parameter + | `x` and `y` all reference this parameter T = help: consider casting both fn items to fn pointers using `as fn()` error[E0308]: mismatched types --> $DIR/fn-item-type.rs:45:19 | LL | eq(foo::, bar:: as fn(isize) -> isize); - | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn item, found fn pointer - | | + | -- --------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn item, found fn pointer + | | | + | | expected all arguments to be this fn item type because they need to match the type of this parameter | arguments to this function are incorrect | = note: expected fn item `fn(_) -> _ {foo::}` @@ -85,7 +102,10 @@ note: function defined here --> $DIR/fn-item-type.rs:11:4 | LL | fn eq(x: T, y: T) {} - | ^^ ---- + | ^^ - ---- ---- this parameter needs to match the fn item type of `x` + | | | + | | `y` needs to match the fn item type of this parameter + | `x` and `y` all reference this parameter T error: aborting due to 5 previous errors diff --git a/tests/ui/issues/issue-1451.rs b/tests/ui/fn/issue-1451.rs similarity index 100% rename from tests/ui/issues/issue-1451.rs rename to tests/ui/fn/issue-1451.rs diff --git a/tests/ui/issues/issue-1900.rs b/tests/ui/fn/issue-1900.rs similarity index 100% rename from tests/ui/issues/issue-1900.rs rename to tests/ui/fn/issue-1900.rs diff --git a/tests/ui/issues/issue-1900.stderr b/tests/ui/fn/issue-1900.stderr similarity index 100% rename from tests/ui/issues/issue-1900.stderr rename to tests/ui/fn/issue-1900.stderr diff --git a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr index 06ffff057f90d..d913b2e91ca0e 100644 --- a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr +++ b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr @@ -6,7 +6,7 @@ LL | cmp_eq | = note: cannot satisfy `_: Scalar` note: required by a bound in `cmp_eq` - --> $DIR/ambig-hr-projection-issue-93340.rs:9:22 + --> $DIR/ambig-hr-projection-issue-93340.rs:10:22 | LL | fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefType<'b>) -> O { | ^^^^^^ required by this bound in `cmp_eq` @@ -15,34 +15,6 @@ help: consider specifying the generic arguments LL | cmp_eq:: | +++++++++++ -error[E0275]: overflow evaluating the requirement `impl for<'a, 'b> Fn(::RefType<'a>, ::RefType<'b>) -> O == for<'a, 'b> fn(..., ...) -> ... {cmp_eq::<..., ..., ...>}` - --> $DIR/ambig-hr-projection-issue-93340.rs:16:5 - | -LL | cmp_eq - | ^^^^^^ - -error[E0275]: overflow evaluating the requirement `impl for<'a, 'b> Fn(::RefType<'a>, ::RefType<'b>) -> O == for<'a, 'b> fn(..., ...) -> ... {cmp_eq::<..., ..., ...>}` - --> $DIR/ambig-hr-projection-issue-93340.rs:16:5 - | -LL | cmp_eq - | ^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0275]: overflow evaluating the requirement `for<'a, 'b> fn(::RefType<'a>, <_ as Scalar>::RefType<'b>) -> _ {cmp_eq::} <: ...` - --> $DIR/ambig-hr-projection-issue-93340.rs:14:51 - | -LL | ) -> impl Fn(A::RefType<'_>, B::RefType<'_>) -> O { - | ___________________________________________________^ -LL | | -LL | | cmp_eq -LL | | -LL | | -LL | | -LL | | } - | |_^ - -error: aborting due to 4 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0275, E0283. -For more information about an error, try `rustc --explain E0275`. +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.old.stderr b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.old.stderr index df2ec4ab182aa..d913b2e91ca0e 100644 --- a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.old.stderr +++ b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.old.stderr @@ -6,7 +6,7 @@ LL | cmp_eq | = note: cannot satisfy `_: Scalar` note: required by a bound in `cmp_eq` - --> $DIR/ambig-hr-projection-issue-93340.rs:9:22 + --> $DIR/ambig-hr-projection-issue-93340.rs:10:22 | LL | fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefType<'b>) -> O { | ^^^^^^ required by this bound in `cmp_eq` diff --git a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.rs b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.rs index 4d8ea9d8d4811..acfebad38db0c 100644 --- a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.rs +++ b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.rs @@ -1,4 +1,5 @@ //@ revisions: old next +//@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver pub trait Scalar: 'static { type RefType<'a>: ScalarRef<'a>; @@ -12,11 +13,8 @@ fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefT fn build_expression( ) -> impl Fn(A::RefType<'_>, B::RefType<'_>) -> O { - //[next]~^ ERROR overflow evaluating the requirement cmp_eq //~^ ERROR type annotations needed - //[next]~| ERROR overflow evaluating the requirement - //[next]~| ERROR overflow evaluating the requirement } fn main() {} diff --git a/tests/ui/higher-ranked/builtin-closure-like-bounds.rs b/tests/ui/higher-ranked/builtin-closure-like-bounds.rs new file mode 100644 index 0000000000000..dee290cc4396a --- /dev/null +++ b/tests/ui/higher-ranked/builtin-closure-like-bounds.rs @@ -0,0 +1,58 @@ +//@ edition:2024 +//@ compile-flags: -Zunstable-options +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ check-pass + +// Makes sure that we support closure/coroutine goals where the signature of +// the item references higher-ranked lifetimes from the *predicate* binder, +// not its own internal signature binder. +// +// This was fixed in . + +#![feature(unboxed_closures, gen_blocks)] + +trait Dispatch { + fn dispatch(self); +} + +struct Fut(T); +impl Fn<(&'a (),)>> Dispatch for Fut +where + for<'a> >::Output: Future, +{ + fn dispatch(self) { + (self.0)(&()); + } +} + +struct Gen(T); +impl Fn<(&'a (),)>> Dispatch for Gen +where + for<'a> >::Output: Iterator, +{ + fn dispatch(self) { + (self.0)(&()); + } +} + +struct Closure(T); +impl Fn<(&'a (),)>> Dispatch for Closure +where + for<'a> >::Output: Fn<(&'a (),)>, +{ + fn dispatch(self) { + (self.0)(&())(&()); + } +} + +fn main() { + async fn foo(_: &()) {} + Fut(foo).dispatch(); + + gen fn bar(_: &()) {} + Gen(bar).dispatch(); + + fn uwu<'a>(x: &'a ()) -> impl Fn(&'a ()) { |_| {} } + Closure(uwu).dispatch(); +} diff --git a/tests/ui/higher-ranked/closure-bound-codegen-ice.rs b/tests/ui/higher-ranked/closure-bound-codegen-ice.rs new file mode 100644 index 0000000000000..4d7ae12d7a742 --- /dev/null +++ b/tests/ui/higher-ranked/closure-bound-codegen-ice.rs @@ -0,0 +1,33 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ build-pass + +// Regression test for incomplete handling of Fn-trait goals, +// fixed in #122267. + +trait Trait { + type Assoc<'a>: FnOnce(&'a ()); +} + +impl Trait for () { + type Assoc<'a> = fn(&'a ()); +} + +trait Indir { + fn break_me() {} +} + +impl Indir for F +where + for<'a> F::Assoc<'a>: FnOnce(&'a ()), +{ + fn break_me() {} +} + +fn foo() { + F::break_me() +} + +fn main() { + foo::<()>(); +} diff --git a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-1.rs b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-1.rs new file mode 100644 index 0000000000000..b448f0bdc7778 --- /dev/null +++ b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-1.rs @@ -0,0 +1,28 @@ +// cc #119820 + +trait Trait {} + +impl Trait for &T {} +impl Trait for u32 {} + +fn hr_bound() +where + for<'a> &'a T: Trait, +{ +} + +fn foo() +where + T: Trait, + for<'a> &'a &'a T: Trait, +{ + // We get a universe error when using the `param_env` candidate + // but are able to successfully use the impl candidate. Without + // the leak check both candidates may apply and we prefer the + // `param_env` candidate in winnowing. + hr_bound::<&T>(); + //~^ ERROR the parameter type `T` may not live long enough + //~| ERROR implementation of `Trait` is not general enough +} + +fn main() {} diff --git a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-1.stderr b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-1.stderr new file mode 100644 index 0000000000000..febe252d7d1d5 --- /dev/null +++ b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-1.stderr @@ -0,0 +1,26 @@ +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/candidate-from-env-universe-err-1.rs:23:5 + | +LL | hr_bound::<&T>(); + | ^^^^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | T: Trait + 'static, + | +++++++++ + +error: implementation of `Trait` is not general enough + --> $DIR/candidate-from-env-universe-err-1.rs:23:5 + | +LL | hr_bound::<&T>(); + | ^^^^^^^^^^^^^^ implementation of `Trait` is not general enough + | + = note: `Trait` would have to be implemented for the type `&'0 &T`, for any lifetime `'0`... + = note: ...but `Trait` is actually implemented for the type `&'1 &'1 T`, for some specific lifetime `'1` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-2.current.stderr b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-2.current.stderr new file mode 100644 index 0000000000000..22ce87c024861 --- /dev/null +++ b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-2.current.stderr @@ -0,0 +1,25 @@ +error: lifetime may not live long enough + --> $DIR/candidate-from-env-universe-err-2.rs:14:5 + | +LL | fn not_hr<'a, T: for<'b> Trait<'a, 'b> + OtherTrait<'static>>() { + | -- lifetime `'a` defined here +LL | impl_hr::(); + | ^^^^^^^^^^^^ requires that `'a` must outlive `'static` + | +note: due to current limitations in the borrow checker, this implies a `'static` lifetime + --> $DIR/candidate-from-env-universe-err-2.rs:11:19 + | +LL | fn impl_hr<'b, T: for<'a> Trait<'a, 'b>>() {} + | ^^^^^^^^^^^^^^^^^^^^^ + +error: implementation of `Trait` is not general enough + --> $DIR/candidate-from-env-universe-err-2.rs:14:5 + | +LL | impl_hr::(); + | ^^^^^^^^^^^^ implementation of `Trait` is not general enough + | + = note: `T` must implement `Trait<'0, '_>`, for any lifetime `'0`... + = note: ...but it actually implements `Trait<'1, '_>`, for some specific lifetime `'1` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-2.next.stderr b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-2.next.stderr new file mode 100644 index 0000000000000..a61bc748bea2d --- /dev/null +++ b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-2.next.stderr @@ -0,0 +1,19 @@ +error[E0277]: the trait bound `for<'a> T: Trait<'a, '_>` is not satisfied + --> $DIR/candidate-from-env-universe-err-2.rs:14:5 + | +LL | impl_hr::(); + | ^^^^^^^^^^^^^^ the trait `for<'a> Trait<'a, '_>` is not implemented for `T` + | +note: required by a bound in `impl_hr` + --> $DIR/candidate-from-env-universe-err-2.rs:11:19 + | +LL | fn impl_hr<'b, T: for<'a> Trait<'a, 'b>>() {} + | ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `impl_hr` +help: consider further restricting this bound + | +LL | fn not_hr<'a, T: for<'b> Trait<'a, 'b> + OtherTrait<'static> + for<'a> Trait<'a, '_>>() { + | +++++++++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-2.old.stderr b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-2.old.stderr new file mode 100644 index 0000000000000..29a72b1c1b641 --- /dev/null +++ b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-2.old.stderr @@ -0,0 +1,26 @@ +error: lifetime may not live long enough + --> $DIR/candidate-from-env-universe-err-2.rs:14:5 + | +LL | fn not_hr<'a, T: for<'b> Trait<'a, 'b> + OtherTrait<'static>>() { + | -- lifetime `'a` defined here +LL | impl_hr::(); + | ^^^^^^^^^^^^ requires that `'a` must outlive `'static` + | +note: due to current limitations in the borrow checker, this implies a `'static` lifetime + --> $DIR/candidate-from-env-universe-err-2.rs:11:19 + | +LL | fn impl_hr<'b, T: for<'a> Trait<'a, 'b>>() {} + | ^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/candidate-from-env-universe-err-2.rs:14:5 + | +LL | impl_hr::(); + | ^^^^^^^^^^^^ one type is more general than the other + | + = note: expected trait `for<'a> Trait<'a, '_>` + found trait `for<'b> Trait<'_, 'b>` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-2.rs b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-2.rs new file mode 100644 index 0000000000000..56fa70469ccf8 --- /dev/null +++ b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-2.rs @@ -0,0 +1,20 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver + +// cc #119820 + +trait Trait<'a, 'b> {} + +trait OtherTrait<'b> {} +impl<'a, 'b, T: OtherTrait<'b>> Trait<'a, 'b> for T {} + +fn impl_hr<'b, T: for<'a> Trait<'a, 'b>>() {} + +fn not_hr<'a, T: for<'b> Trait<'a, 'b> + OtherTrait<'static>>() { + impl_hr::(); + //[next]~^ ERROR the trait bound `for<'a> T: Trait<'a, '_>` is not satisfied + //[current]~^^ERROR lifetime may not live long enough + //[current]~| ERROR implementation of `Trait` is not general enough +} + +fn main() {} diff --git a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.current.stderr b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.current.stderr new file mode 100644 index 0000000000000..bb0b2de788e83 --- /dev/null +++ b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.current.stderr @@ -0,0 +1,54 @@ +error: implementation of `Trait` is not general enough + --> $DIR/candidate-from-env-universe-err-project.rs:28:5 + | +LL | trait_bound::(); + | ^^^^^^^^^^^^^^^^^^ implementation of `Trait` is not general enough + | + = note: `T` must implement `Trait<'0>`, for any lifetime `'0`... + = note: ...but it actually implements `Trait<'static>` + +error: implementation of `Trait` is not general enough + --> $DIR/candidate-from-env-universe-err-project.rs:39:5 + | +LL | projection_bound::(); + | ^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Trait` is not general enough + | + = note: `T` must implement `Trait<'0>`, for any lifetime `'0`... + = note: ...but it actually implements `Trait<'static>` + +error[E0308]: mismatched types + --> $DIR/candidate-from-env-universe-err-project.rs:39:5 + | +LL | projection_bound::(); + | ^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other + | + = note: expected associated type `>::Assoc` + found associated type `>::Assoc` +note: the lifetime requirement is introduced here + --> $DIR/candidate-from-env-universe-err-project.rs:18:42 + | +LL | fn projection_bound Trait<'a, Assoc = usize>>() {} + | ^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/candidate-from-env-universe-err-project.rs:55:30 + | +LL | let _higher_ranked_norm: for<'a> fn(>::Assoc) = |_| (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other + | + = note: expected associated type `>::Assoc` + found associated type `>::Assoc` + +error[E0308]: mismatched types + --> $DIR/candidate-from-env-universe-err-project.rs:55:30 + | +LL | let _higher_ranked_norm: for<'a> fn(>::Assoc) = |_| (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other + | + = note: expected associated type `>::Assoc` + found associated type `>::Assoc` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.next.stderr b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.next.stderr new file mode 100644 index 0000000000000..2804d5bbe9408 --- /dev/null +++ b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.next.stderr @@ -0,0 +1,67 @@ +error[E0277]: the trait bound `for<'a> T: Trait<'a>` is not satisfied + --> $DIR/candidate-from-env-universe-err-project.rs:28:19 + | +LL | trait_bound::(); + | ^ the trait `for<'a> Trait<'a>` is not implemented for `T` + | +note: required by a bound in `trait_bound` + --> $DIR/candidate-from-env-universe-err-project.rs:17:19 + | +LL | fn trait_bound Trait<'a>>() {} + | ^^^^^^^^^^^^^^^^^ required by this bound in `trait_bound` +help: consider further restricting this bound + | +LL | fn function1 + for<'a> Trait<'a>>() { + | +++++++++++++++++++ + +error[E0277]: the trait bound `for<'a> T: Trait<'a>` is not satisfied + --> $DIR/candidate-from-env-universe-err-project.rs:39:24 + | +LL | projection_bound::(); + | ^ the trait `for<'a> Trait<'a>` is not implemented for `T` + | +note: required by a bound in `projection_bound` + --> $DIR/candidate-from-env-universe-err-project.rs:18:24 + | +LL | fn projection_bound Trait<'a, Assoc = usize>>() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `projection_bound` +help: consider further restricting this bound + | +LL | fn function2 + for<'a> Trait<'a>>() { + | +++++++++++++++++++ + +error[E0271]: type mismatch resolving `>::Assoc == usize` + --> $DIR/candidate-from-env-universe-err-project.rs:39:24 + | +LL | projection_bound::(); + | ^ type mismatch resolving `>::Assoc == usize` + | +note: types differ + --> $DIR/candidate-from-env-universe-err-project.rs:14:18 + | +LL | type Assoc = usize; + | ^^^^^ +note: required by a bound in `projection_bound` + --> $DIR/candidate-from-env-universe-err-project.rs:18:42 + | +LL | fn projection_bound Trait<'a, Assoc = usize>>() {} + | ^^^^^^^^^^^^^ required by this bound in `projection_bound` + +error: higher-ranked subtype error + --> $DIR/candidate-from-env-universe-err-project.rs:55:30 + | +LL | let _higher_ranked_norm: for<'a> fn(>::Assoc) = |_| (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: higher-ranked subtype error + --> $DIR/candidate-from-env-universe-err-project.rs:55:30 + | +LL | let _higher_ranked_norm: for<'a> fn(>::Assoc) = |_| (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0271, E0277. +For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.rs b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.rs new file mode 100644 index 0000000000000..2f53bd019b78c --- /dev/null +++ b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.rs @@ -0,0 +1,62 @@ +//@ revisions: next current +//@[next] compile-flags: -Znext-solver + +// cc #119820 the previous behavior here was inconsistent as we discarded +// the where-bound candidate for trait goals due to the leak check, but did +// not do so for projection candidates and during normalization. +// +// This results in an inconsistency between `Trait` and `Projection` goals as +// normalizing always constraints the normalized-to term. +trait Trait<'a> { + type Assoc; +} +impl<'a, T> Trait<'a> for T { + type Assoc = usize; +} + +fn trait_bound Trait<'a>>() {} +fn projection_bound Trait<'a, Assoc = usize>>() {} + +// We use a function with a trivial where-bound which is more +// restrictive than the impl. +fn function1>() { + // err + // + // Proving `for<'a> T: Trait<'a>` using the where-bound does not + // result in a leak check failure even though it does not apply. + // We prefer env candidates over impl candidatescausing this to succeed. + trait_bound::(); + //[next]~^ ERROR the trait bound `for<'a> T: Trait<'a>` is not satisfied + //[current]~^^ ERROR implementation of `Trait` is not general enough +} + +fn function2>() { + // err + // + // Proving the `Projection` goal `for<'a> T: Trait<'a, Assoc = usize>` + // does not use the leak check when trying the where-bound, causing us + // to prefer it over the impl, resulting in a placeholder error. + projection_bound::(); + //[next]~^ ERROR type mismatch resolving `>::Assoc == usize` + //[next]~| ERROR the trait bound `for<'a> T: Trait<'a>` is not satisfied + //[current]~^^^ ERROR implementation of `Trait` is not general enough + //[current]~| ERROR mismatched types +} + +fn function3>() { + // err + // + // Trying to normalize the type `for<'a> fn(>::Assoc)` + // only gets to `>::Assoc` once `'a` has been already + // instantiated, causing us to prefer the where-bound over the impl + // resulting in a placeholder error. Even if were were to also use the + // leak check during candidate selection for normalization, this + // case would still not compile. + let _higher_ranked_norm: for<'a> fn(>::Assoc) = |_| (); + //[next]~^ ERROR higher-ranked subtype error + //[next]~| ERROR higher-ranked subtype error + //[current]~^^^ ERROR mismatched types + //[current]~| ERROR mismatched types +} + +fn main() {} diff --git a/tests/ui/higher-ranked/leak-check-in-selection.rs b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-1.rs similarity index 100% rename from tests/ui/higher-ranked/leak-check-in-selection.rs rename to tests/ui/higher-ranked/leak-check/leak-check-in-selection-1.rs diff --git a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-2.next.stderr b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-2.next.stderr new file mode 100644 index 0000000000000..a840304e49c1f --- /dev/null +++ b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-2.next.stderr @@ -0,0 +1,23 @@ +error[E0283]: type annotations needed + --> $DIR/leak-check-in-selection-2.rs:16:5 + | +LL | impls_trait::<(), _>(); + | ^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `U` declared on the function `impls_trait` + | +note: multiple `impl`s satisfying `for<'a> (): Trait<&'a str, _>` found + --> $DIR/leak-check-in-selection-2.rs:9:1 + | +LL | impl<'a> Trait<&'a str, &'a str> for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | impl<'a> Trait<&'a str, String> for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `impls_trait` + --> $DIR/leak-check-in-selection-2.rs:13:19 + | +LL | fn impls_trait Trait<&'a str, U>, U>() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `impls_trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-2.old.stderr b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-2.old.stderr new file mode 100644 index 0000000000000..a840304e49c1f --- /dev/null +++ b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-2.old.stderr @@ -0,0 +1,23 @@ +error[E0283]: type annotations needed + --> $DIR/leak-check-in-selection-2.rs:16:5 + | +LL | impls_trait::<(), _>(); + | ^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `U` declared on the function `impls_trait` + | +note: multiple `impl`s satisfying `for<'a> (): Trait<&'a str, _>` found + --> $DIR/leak-check-in-selection-2.rs:9:1 + | +LL | impl<'a> Trait<&'a str, &'a str> for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | impl<'a> Trait<&'a str, String> for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `impls_trait` + --> $DIR/leak-check-in-selection-2.rs:13:19 + | +LL | fn impls_trait Trait<&'a str, U>, U>() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `impls_trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-2.rs b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-2.rs new file mode 100644 index 0000000000000..48dd569f201b9 --- /dev/null +++ b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-2.rs @@ -0,0 +1,18 @@ +//@ revisions: old next +//@[next] compile-flags: -Znext-solver + +// cc #119820 + +trait Trait {} + +// using this impl results in a higher-ranked region error. +impl<'a> Trait<&'a str, &'a str> for () {} + +impl<'a> Trait<&'a str, String> for () {} + +fn impls_trait Trait<&'a str, U>, U>() {} + +fn main() { + impls_trait::<(), _>(); + //~^ ERROR type annotations needed +} diff --git a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-3.next.stderr b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-3.next.stderr new file mode 100644 index 0000000000000..8a8118dea859b --- /dev/null +++ b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-3.next.stderr @@ -0,0 +1,35 @@ +error[E0283]: type annotations needed + --> $DIR/leak-check-in-selection-3.rs:18:5 + | +LL | impls_leak::>(); + | ^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `impls_leak` + | +note: multiple `impl`s satisfying `for<'a> Box<_>: Leak<'a>` found + --> $DIR/leak-check-in-selection-3.rs:9:1 + | +LL | impl Leak<'_> for Box {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl Leak<'static> for Box {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `impls_leak` + --> $DIR/leak-check-in-selection-3.rs:12:18 + | +LL | fn impls_leak Leak<'a>>() {} + | ^^^^^^^^^^^^^^^^ required by this bound in `impls_leak` + +error[E0283]: type annotations needed + --> $DIR/leak-check-in-selection-3.rs:35:5 + | +LL | impls_indirect_leak::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `impls_indirect_leak` + | + = note: cannot satisfy `for<'a> Box<_>: IndirectLeak<'a>` +note: required by a bound in `impls_indirect_leak` + --> $DIR/leak-check-in-selection-3.rs:25:27 + | +LL | fn impls_indirect_leak IndirectLeak<'a>>() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `impls_indirect_leak` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-3.old.stderr b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-3.old.stderr new file mode 100644 index 0000000000000..662a06537401a --- /dev/null +++ b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-3.old.stderr @@ -0,0 +1,48 @@ +error[E0283]: type annotations needed + --> $DIR/leak-check-in-selection-3.rs:18:5 + | +LL | impls_leak::>(); + | ^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `impls_leak` + | +note: multiple `impl`s satisfying `for<'a> Box<_>: Leak<'a>` found + --> $DIR/leak-check-in-selection-3.rs:9:1 + | +LL | impl Leak<'_> for Box {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl Leak<'static> for Box {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `impls_leak` + --> $DIR/leak-check-in-selection-3.rs:12:18 + | +LL | fn impls_leak Leak<'a>>() {} + | ^^^^^^^^^^^^^^^^ required by this bound in `impls_leak` + +error[E0283]: type annotations needed + --> $DIR/leak-check-in-selection-3.rs:35:5 + | +LL | impls_indirect_leak::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `impls_indirect_leak` + | +note: multiple `impl`s satisfying `Box<_>: Leak<'_>` found + --> $DIR/leak-check-in-selection-3.rs:9:1 + | +LL | impl Leak<'_> for Box {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl Leak<'static> for Box {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required for `Box<_>` to implement `for<'a> IndirectLeak<'a>` + --> $DIR/leak-check-in-selection-3.rs:23:23 + | +LL | impl<'a, T: Leak<'a>> IndirectLeak<'a> for T {} + | -------- ^^^^^^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here +note: required by a bound in `impls_indirect_leak` + --> $DIR/leak-check-in-selection-3.rs:25:27 + | +LL | fn impls_indirect_leak IndirectLeak<'a>>() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `impls_indirect_leak` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-3.rs b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-3.rs new file mode 100644 index 0000000000000..9e99b6c527d9d --- /dev/null +++ b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-3.rs @@ -0,0 +1,39 @@ +//@ revisions: old next +//@[next] compile-flags: -Znext-solver + +// cc #119820, the previous behavior here was inconsistent, +// using the leak check to guide inference for `for<'a> Box<_>: Leak<'a>` +// but not for `for<'a> Box<_>: IndirectLeak<'a>` + +trait Leak<'a> {} +impl Leak<'_> for Box {} +impl Leak<'static> for Box {} + +fn impls_leak Leak<'a>>() {} +fn direct() { + // ok + // + // The `Box` impls fails the leak check, + // meaning that we apply the `Box` impl. + impls_leak::>(); + //~^ ERROR type annotations needed +} + +trait IndirectLeak<'a> {} +impl<'a, T: Leak<'a>> IndirectLeak<'a> for T {} + +fn impls_indirect_leak IndirectLeak<'a>>() {} +fn indirect() { + // error: type annotations needed + // + // While the `Box` impl would fail the leak check + // we have already instantiated the binder while applying + // the generic `IndirectLeak` impl, so during candidate + // selection of `Leak` we do not detect the placeholder error. + // Evaluation of `Box<_>: Leak<'!a>` is therefore ambiguous, + // resulting in `for<'a> Box<_>: Leak<'a>` also being ambiguous. + impls_indirect_leak::>(); + //~^ ERROR type annotations needed +} + +fn main() {} diff --git a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-4-hr-nested.rs b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-4-hr-nested.rs new file mode 100644 index 0000000000000..8d87bdd064a06 --- /dev/null +++ b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-4-hr-nested.rs @@ -0,0 +1,29 @@ +//@ revisions: old next +//@[next] compile-flags: -Znext-solver +//@ check-pass + +// cc #119820. While the leak check does not consider the binder +// of the current goal, leaks from higher-ranked nested goals are +// considered. +// +// We enter and exit the binder of the nested goal while evaluating +// the candidate. + +trait LeakCheckFailure<'a> {} +impl LeakCheckFailure<'static> for () {} + +trait Trait {} +impl Trait for () where for<'a> (): LeakCheckFailure<'a> {} +impl Trait for () {} +fn impls_trait, U>() {} +fn main() { + // ok + // + // It does not matter whether candidate assembly + // considers the placeholders from higher-ranked goal. + // + // Either `for<'a> (): LeakCheckFailure<'a>` has no applicable + // candidate or it has a single applicable candidate which then later + // results in an error. This allows us to infer `U` to `u16`. + impls_trait::<(), _>() +} diff --git a/tests/ui/higher-ranked/trait-bounds/fn-ptr.classic.stderr b/tests/ui/higher-ranked/trait-bounds/fn-ptr.classic.stderr deleted file mode 100644 index b322ea41c436d..0000000000000 --- a/tests/ui/higher-ranked/trait-bounds/fn-ptr.classic.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0277]: expected a `Fn(&'w ())` closure, found `fn(&'w ())` - --> $DIR/fn-ptr.rs:12:5 - | -LL | ice(); - | ^^^^^ expected an `Fn(&'w ())` closure, found `fn(&'w ())` - | - = help: the trait `for<'w> Fn<(&'w (),)>` is not implemented for `fn(&'w ())` -note: required by a bound in `ice` - --> $DIR/fn-ptr.rs:7:25 - | -LL | fn ice() - | --- required by a bound in this function -LL | where -LL | for<'w> fn(&'w ()): Fn(&'w ()), - | ^^^^^^^^^^ required by this bound in `ice` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/higher-ranked/trait-bounds/fn-ptr.current.stderr b/tests/ui/higher-ranked/trait-bounds/fn-ptr.current.stderr deleted file mode 100644 index f3583cd218b8c..0000000000000 --- a/tests/ui/higher-ranked/trait-bounds/fn-ptr.current.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0277]: expected a `Fn(&'w ())` closure, found `fn(&'w ())` - --> $DIR/fn-ptr.rs:13:5 - | -LL | ice(); - | ^^^^^ expected an `Fn(&'w ())` closure, found `fn(&'w ())` - | - = help: the trait `for<'w> Fn<(&'w (),)>` is not implemented for `fn(&'w ())` -note: required by a bound in `ice` - --> $DIR/fn-ptr.rs:8:25 - | -LL | fn ice() - | --- required by a bound in this function -LL | where -LL | for<'w> fn(&'w ()): Fn(&'w ()), - | ^^^^^^^^^^ required by this bound in `ice` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/higher-ranked/trait-bounds/fn-ptr.rs b/tests/ui/higher-ranked/trait-bounds/fn-ptr.rs index 9298c10c3417c..7a4c15f4d4b15 100644 --- a/tests/ui/higher-ranked/trait-bounds/fn-ptr.rs +++ b/tests/ui/higher-ranked/trait-bounds/fn-ptr.rs @@ -1,7 +1,7 @@ //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[next] check-pass +//@ check-pass fn ice() where @@ -11,5 +11,4 @@ where fn main() { ice(); - //[current]~^ ERROR expected a `Fn(&'w ())` closure, found `fn(&'w ())` } diff --git a/tests/ui/higher-ranked/trait-bounds/future.classic.stderr b/tests/ui/higher-ranked/trait-bounds/future.classic.stderr deleted file mode 100644 index ef31b7266c7ef..0000000000000 --- a/tests/ui/higher-ranked/trait-bounds/future.classic.stderr +++ /dev/null @@ -1,6 +0,0 @@ -error: the compiler unexpectedly panicked. this is a bug. - -query stack during panic: -#0 [evaluate_obligation] evaluating trait selection obligation `for<'a> {async fn body@$DIR/future.rs:32:35: 34:2}: core::future::future::Future` -#1 [codegen_select_candidate] computing candidate for `` -end of query stack diff --git a/tests/ui/higher-ranked/trait-bounds/future.current.stderr b/tests/ui/higher-ranked/trait-bounds/future.current.stderr deleted file mode 100644 index 673bc48a424e7..0000000000000 --- a/tests/ui/higher-ranked/trait-bounds/future.current.stderr +++ /dev/null @@ -1,6 +0,0 @@ -error: the compiler unexpectedly panicked. this is a bug. - -query stack during panic: -#0 [evaluate_obligation] evaluating trait selection obligation `for<'a> {async fn body of strlen()}: core::future::future::Future` -#1 [codegen_select_candidate] computing candidate for `` -end of query stack diff --git a/tests/ui/higher-ranked/trait-bounds/future.rs b/tests/ui/higher-ranked/trait-bounds/future.rs index 4b52f04dbe05f..7105015b69012 100644 --- a/tests/ui/higher-ranked/trait-bounds/future.rs +++ b/tests/ui/higher-ranked/trait-bounds/future.rs @@ -3,14 +3,7 @@ //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[next] check-pass -//@[current] known-bug: #112347 -//@[current] build-fail -//@[current] failure-status: 101 -//@[current] normalize-stderr-test "note: .*\n\n" -> "" -//@[current] normalize-stderr-test "thread 'rustc' panicked.*\n.*\n" -> "" -//@[current] normalize-stderr-test "(error: internal compiler error: [^:]+):\d+:\d+: " -> "$1:LL:CC: " -//@[current] rustc-env:RUST_BACKTRACE=0 +//@ check-pass #![feature(unboxed_closures)] diff --git a/tests/ui/higher-ranked/trait-bounds/issue-30786.rs b/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-1.rs similarity index 86% rename from tests/ui/higher-ranked/trait-bounds/issue-30786.rs rename to tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-1.rs index ffb2b306ae76a..799df8cae9fd7 100644 --- a/tests/ui/higher-ranked/trait-bounds/issue-30786.rs +++ b/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-1.rs @@ -1,6 +1,6 @@ //@ normalize-stderr-test: "long-type-\d+" -> "long-type-hash" -// rust-lang/rust#30786: the use of `for<'b> &'b mut A: Stream &'b mut A: Stream` // should act as assertion that item does not borrow from its stream; // but an earlier buggy rustc allowed `.map(|x: &_| x)` which does // have such an item. @@ -97,10 +97,6 @@ where impl StreamExt for T where for<'a> &'a mut T: Stream {} -fn identity(x: &T) -> &T { - x -} - fn variant1() { let source = Repeat(10); @@ -118,19 +114,7 @@ fn variant1() { // guess. let map = source.mapx(|x: &_| x); let filter = map.filterx(|x: &_| true); - //~^ ERROR the method -} - -fn variant2() { - let source = Repeat(10); - - // Here, we use a function, which is not subject to the vagaries - // of closure signature inference. In this case, we get the error - // on `countx` as, I think, the test originally expected. - let map = source.mapx(identity); - let filter = map.filterx(|x: &_| true); - let count = filter.countx(); - //~^ ERROR the method + //~^ ERROR the method `filterx` exists for struct } fn main() {} diff --git a/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-1.stderr b/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-1.stderr new file mode 100644 index 0000000000000..ae364de8cc061 --- /dev/null +++ b/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-1.stderr @@ -0,0 +1,27 @@ +error[E0599]: the method `filterx` exists for struct `Map`, but its trait bounds were not satisfied + --> $DIR/hrtb-doesnt-borrow-self-1.rs:116:22 + | +LL | pub struct Map { + | -------------------- method `filterx` not found for this struct because it doesn't satisfy `_: StreamExt` +... +LL | let filter = map.filterx(|x: &_| true); + | ^^^^^^^ method cannot be called due to unsatisfied trait bounds + | +note: the following trait bounds were not satisfied: + `&'a mut &Map: Stream` + `&'a mut &mut Map: Stream` + `&'a mut Map: Stream` + --> $DIR/hrtb-doesnt-borrow-self-1.rs:98:50 + | +LL | impl StreamExt for T where for<'a> &'a mut T: Stream {} + | --------- - ^^^^^^ unsatisfied trait bound introduced here + = help: items from traits can only be used if the trait is implemented and in scope +note: `StreamExt` defines an item `filterx`, perhaps you need to implement it + --> $DIR/hrtb-doesnt-borrow-self-1.rs:66:1 + | +LL | pub trait StreamExt + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-2.rs b/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-2.rs new file mode 100644 index 0000000000000..92e2e7f796eaa --- /dev/null +++ b/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-2.rs @@ -0,0 +1,116 @@ +//@ normalize-stderr-test: "long-type-\d+" -> "long-type-hash" + +// rust-lang/rust#30786: the use of `for<'b> &'b mut A: Stream Option; +} + +// Example stream +pub struct Repeat(u64); + +impl<'a> Stream for &'a mut Repeat { + type Item = &'a u64; + fn next(self) -> Option { + Some(&self.0) + } +} + +pub struct Map { + stream: S, + func: F, +} + +impl<'a, A, F, T> Stream for &'a mut Map +where + &'a mut A: Stream, + F: FnMut(<&'a mut A as Stream>::Item) -> T, +{ + type Item = T; + fn next(self) -> Option { + match self.stream.next() { + Some(item) => Some((self.func)(item)), + None => None, + } + } +} + +pub struct Filter { + stream: S, + func: F, +} + +impl<'a, A, F, T> Stream for &'a mut Filter +where + for<'b> &'b mut A: Stream, // <---- BAD + F: FnMut(&T) -> bool, +{ + type Item = <&'a mut A as Stream>::Item; + fn next(self) -> Option { + while let Some(item) = self.stream.next() { + if (self.func)(&item) { + return Some(item); + } + } + None + } +} + +pub trait StreamExt +where + for<'b> &'b mut Self: Stream, +{ + fn mapx(self, func: F) -> Map + where + Self: Sized, + for<'a> &'a mut Map: Stream, + { + Map { func: func, stream: self } + } + + fn filterx(self, func: F) -> Filter + where + Self: Sized, + for<'a> &'a mut Filter: Stream, + { + Filter { func: func, stream: self } + } + + fn countx(mut self) -> usize + where + Self: Sized, + { + let mut count = 0; + while let Some(_) = self.next() { + count += 1; + } + count + } +} + +impl StreamExt for T where for<'a> &'a mut T: Stream {} + +fn identity(x: &T) -> &T { + x +} + +fn variant2() { + let source = Repeat(10); + + // Here, we use a function, which is not subject to the vagaries + // of closure signature inference. In this case, we get the error + // on `countx` as, I think, the test originally expected. + let map = source.mapx(identity); + let filter = map.filterx(|x: &_| true); + let count = filter.countx(); + //~^ ERROR the method +} + +fn main() {} diff --git a/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-2.stderr b/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-2.stderr new file mode 100644 index 0000000000000..eeb4e12fa8b31 --- /dev/null +++ b/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-2.stderr @@ -0,0 +1,27 @@ +error[E0599]: the method `countx` exists for struct `Filter &u64 {identity::}>, {closure@hrtb-doesnt-borrow-self-2.rs:111:30}>`, but its trait bounds were not satisfied + --> $DIR/hrtb-doesnt-borrow-self-2.rs:112:24 + | +LL | pub struct Filter { + | ----------------------- method `countx` not found for this struct because it doesn't satisfy `_: StreamExt` +... +LL | let count = filter.countx(); + | ^^^^^^ method cannot be called due to unsatisfied trait bounds + | +note: the following trait bounds were not satisfied: + `&'a mut &Filter fn(&'a u64) -> &'a u64 {identity::}>, {closure@$DIR/hrtb-doesnt-borrow-self-2.rs:111:30: 111:37}>: Stream` + `&'a mut &mut Filter fn(&'a u64) -> &'a u64 {identity::}>, {closure@$DIR/hrtb-doesnt-borrow-self-2.rs:111:30: 111:37}>: Stream` + `&'a mut Filter fn(&'a u64) -> &'a u64 {identity::}>, {closure@$DIR/hrtb-doesnt-borrow-self-2.rs:111:30: 111:37}>: Stream` + --> $DIR/hrtb-doesnt-borrow-self-2.rs:98:50 + | +LL | impl StreamExt for T where for<'a> &'a mut T: Stream {} + | --------- - ^^^^^^ unsatisfied trait bound introduced here + = help: items from traits can only be used if the trait is implemented and in scope +note: `StreamExt` defines an item `countx`, perhaps you need to implement it + --> $DIR/hrtb-doesnt-borrow-self-2.rs:66:1 + | +LL | pub trait StreamExt + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/higher-ranked/trait-bounds/hrtb-higher-ranker-supertraits-transitive.stderr b/tests/ui/higher-ranked/trait-bounds/hrtb-higher-ranker-supertraits-transitive.stderr index e10da26665ebb..be19bf85bd2f3 100644 --- a/tests/ui/higher-ranked/trait-bounds/hrtb-higher-ranker-supertraits-transitive.stderr +++ b/tests/ui/higher-ranked/trait-bounds/hrtb-higher-ranker-supertraits-transitive.stderr @@ -1,23 +1,11 @@ -error[E0277]: the trait bound `for<'ccx> B: Bar<'ccx>` is not satisfied - --> $DIR/hrtb-higher-ranker-supertraits-transitive.rs:47:26 +error: implementation of `Bar` is not general enough + --> $DIR/hrtb-higher-ranker-supertraits-transitive.rs:47:5 | LL | want_bar_for_any_ccx(b); - | -------------------- ^ the trait `for<'ccx> Bar<'ccx>` is not implemented for `B` - | | - | required by a bound introduced by this call + | ^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Bar` is not general enough | -note: required by a bound in `want_bar_for_any_ccx` - --> $DIR/hrtb-higher-ranker-supertraits-transitive.rs:32:15 - | -LL | fn want_bar_for_any_ccx(b: &B) - | -------------------- required by a bound in this function -LL | where B : for<'ccx> Bar<'ccx> - | ^^^^^^^^^^^^^^^^^^^ required by this bound in `want_bar_for_any_ccx` -help: consider further restricting this bound - | -LL | where B : Qux + for<'ccx> Bar<'ccx> - | +++++++++++++++++++++ + = note: `B` must implement `Bar<'0>`, for any lifetime `'0`... + = note: ...but it actually implements `Bar<'static>` error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/higher-ranked/trait-bounds/hrtb-higher-ranker-supertraits.rs b/tests/ui/higher-ranked/trait-bounds/hrtb-higher-ranker-supertraits.rs index 33e0ec4635b66..70ce580258d43 100644 --- a/tests/ui/higher-ranked/trait-bounds/hrtb-higher-ranker-supertraits.rs +++ b/tests/ui/higher-ranked/trait-bounds/hrtb-higher-ranker-supertraits.rs @@ -1,43 +1,37 @@ // Test a trait (`Bar`) with a higher-ranked supertrait. +#![allow(unconditional_recursion)] -trait Foo<'tcx> -{ +trait Foo<'tcx> { fn foo(&'tcx self) -> &'tcx isize; } -trait Bar<'ccx> - : for<'tcx> Foo<'tcx> -{ +trait Bar<'ccx>: for<'tcx> Foo<'tcx> { fn bar(&'ccx self) -> &'ccx isize; } -fn want_foo_for_some_tcx<'x,F>(f: &'x F) - where F : Foo<'x> -{ +fn want_foo_for_some_tcx<'x, F: Foo<'x>>(f: &'x F) { want_foo_for_some_tcx(f); - want_foo_for_any_tcx(f); //~ ERROR not satisfied + want_foo_for_any_tcx(f); + //~^ ERROR lifetime may not live long enough + //~| ERROR implementation of `Foo` is not general enough } -fn want_foo_for_any_tcx(f: &F) //~ WARN cannot return without recursing - where F : for<'tcx> Foo<'tcx> -{ +fn want_foo_for_any_tcx Foo<'tcx>>(f: &F) { want_foo_for_some_tcx(f); want_foo_for_any_tcx(f); } -fn want_bar_for_some_ccx<'x,B>(b: &B) - where B : Bar<'x> -{ +fn want_bar_for_some_ccx<'x, B: Bar<'x>>(b: &B) { want_foo_for_some_tcx(b); want_foo_for_any_tcx(b); want_bar_for_some_ccx(b); - want_bar_for_any_ccx(b); //~ ERROR not satisfied + want_bar_for_any_ccx(b); + //~^ ERROR lifetime may not live long enough + //~| ERROR implementation of `Bar` is not general enough } -fn want_bar_for_any_ccx(b: &B) //~ WARN cannot return without recursing - where B : for<'ccx> Bar<'ccx> -{ +fn want_bar_for_any_ccx Bar<'ccx>>(b: &B) { want_foo_for_some_tcx(b); want_foo_for_any_tcx(b); diff --git a/tests/ui/higher-ranked/trait-bounds/hrtb-higher-ranker-supertraits.stderr b/tests/ui/higher-ranked/trait-bounds/hrtb-higher-ranker-supertraits.stderr index f220ba6f33893..dd760926ea117 100644 --- a/tests/ui/higher-ranked/trait-bounds/hrtb-higher-ranker-supertraits.stderr +++ b/tests/ui/higher-ranked/trait-bounds/hrtb-higher-ranker-supertraits.stderr @@ -1,68 +1,50 @@ -error[E0277]: the trait bound `for<'tcx> F: Foo<'tcx>` is not satisfied - --> $DIR/hrtb-higher-ranker-supertraits.rs:18:26 +error: lifetime may not live long enough + --> $DIR/hrtb-higher-ranker-supertraits.rs:14:5 | +LL | fn want_foo_for_some_tcx<'x, F: Foo<'x>>(f: &'x F) { + | -- lifetime `'x` defined here +LL | want_foo_for_some_tcx(f); LL | want_foo_for_any_tcx(f); - | -------------------- ^ the trait `for<'tcx> Foo<'tcx>` is not implemented for `F` - | | - | required by a bound introduced by this call + | ^^^^^^^^^^^^^^^^^^^^^^^ requires that `'x` must outlive `'static` | -note: required by a bound in `want_foo_for_any_tcx` - --> $DIR/hrtb-higher-ranker-supertraits.rs:22:15 +note: due to current limitations in the borrow checker, this implies a `'static` lifetime + --> $DIR/hrtb-higher-ranker-supertraits.rs:19:28 | -LL | fn want_foo_for_any_tcx(f: &F) - | -------------------- required by a bound in this function -LL | where F : for<'tcx> Foo<'tcx> - | ^^^^^^^^^^^^^^^^^^^ required by this bound in `want_foo_for_any_tcx` -help: consider further restricting this bound - | -LL | where F : Foo<'x> + for<'tcx> Foo<'tcx> - | +++++++++++++++++++++ +LL | fn want_foo_for_any_tcx Foo<'tcx>>(f: &F) { + | ^^^^^^^^^^^^^^^^^^^ -error[E0277]: the trait bound `for<'ccx> B: Bar<'ccx>` is not satisfied - --> $DIR/hrtb-higher-ranker-supertraits.rs:35:26 - | -LL | want_bar_for_any_ccx(b); - | -------------------- ^ the trait `for<'ccx> Bar<'ccx>` is not implemented for `B` - | | - | required by a bound introduced by this call +error: implementation of `Foo` is not general enough + --> $DIR/hrtb-higher-ranker-supertraits.rs:14:5 | -note: required by a bound in `want_bar_for_any_ccx` - --> $DIR/hrtb-higher-ranker-supertraits.rs:39:15 - | -LL | fn want_bar_for_any_ccx(b: &B) - | -------------------- required by a bound in this function -LL | where B : for<'ccx> Bar<'ccx> - | ^^^^^^^^^^^^^^^^^^^ required by this bound in `want_bar_for_any_ccx` -help: consider further restricting this bound +LL | want_foo_for_any_tcx(f); + | ^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough | -LL | where B : Bar<'x> + for<'ccx> Bar<'ccx> - | +++++++++++++++++++++ + = note: `F` must implement `Foo<'0>`, for any lifetime `'0`... + = note: ...but it actually implements `Foo<'1>`, for some specific lifetime `'1` -warning: function cannot return without recursing - --> $DIR/hrtb-higher-ranker-supertraits.rs:21:1 +error: lifetime may not live long enough + --> $DIR/hrtb-higher-ranker-supertraits.rs:29:5 | -LL | / fn want_foo_for_any_tcx(f: &F) -LL | | where F : for<'tcx> Foo<'tcx> - | |_________________________________^ cannot return without recursing +LL | fn want_bar_for_some_ccx<'x, B: Bar<'x>>(b: &B) { + | -- lifetime `'x` defined here ... -LL | want_foo_for_any_tcx(f); - | ----------------------- recursive call site +LL | want_bar_for_any_ccx(b); + | ^^^^^^^^^^^^^^^^^^^^^^^ requires that `'x` must outlive `'static` + | +note: due to current limitations in the borrow checker, this implies a `'static` lifetime + --> $DIR/hrtb-higher-ranker-supertraits.rs:34:28 | - = help: a `loop` may express intention better if this is on purpose - = note: `#[warn(unconditional_recursion)]` on by default +LL | fn want_bar_for_any_ccx Bar<'ccx>>(b: &B) { + | ^^^^^^^^^^^^^^^^^^^ -warning: function cannot return without recursing - --> $DIR/hrtb-higher-ranker-supertraits.rs:38:1 +error: implementation of `Bar` is not general enough + --> $DIR/hrtb-higher-ranker-supertraits.rs:29:5 | -LL | / fn want_bar_for_any_ccx(b: &B) -LL | | where B : for<'ccx> Bar<'ccx> - | |_________________________________^ cannot return without recursing -... -LL | want_bar_for_any_ccx(b); - | ----------------------- recursive call site +LL | want_bar_for_any_ccx(b); + | ^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Bar` is not general enough | - = help: a `loop` may express intention better if this is on purpose + = note: `B` must implement `Bar<'0>`, for any lifetime `'0`... + = note: ...but it actually implements `Bar<'1>`, for some specific lifetime `'1` -error: aborting due to 2 previous errors; 2 warnings emitted +error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/higher-ranked/trait-bounds/issue-30786.stderr b/tests/ui/higher-ranked/trait-bounds/issue-30786.stderr deleted file mode 100644 index 699a4ecc42bb9..0000000000000 --- a/tests/ui/higher-ranked/trait-bounds/issue-30786.stderr +++ /dev/null @@ -1,51 +0,0 @@ -error[E0599]: the method `filterx` exists for struct `Map`, but its trait bounds were not satisfied - --> $DIR/issue-30786.rs:120:22 - | -LL | pub struct Map { - | -------------------- method `filterx` not found for this struct because it doesn't satisfy `_: StreamExt` -... -LL | let filter = map.filterx(|x: &_| true); - | ^^^^^^^ method cannot be called on `Map` due to unsatisfied trait bounds - | -note: the following trait bounds were not satisfied: - `&'a mut &Map: Stream` - `&'a mut &mut Map: Stream` - `&'a mut Map: Stream` - --> $DIR/issue-30786.rs:98:50 - | -LL | impl StreamExt for T where for<'a> &'a mut T: Stream {} - | --------- - ^^^^^^ unsatisfied trait bound introduced here - = help: items from traits can only be used if the trait is implemented and in scope -note: `StreamExt` defines an item `filterx`, perhaps you need to implement it - --> $DIR/issue-30786.rs:66:1 - | -LL | pub trait StreamExt - | ^^^^^^^^^^^^^^^^^^^ - -error[E0599]: the method `countx` exists for struct `Filter &u64 {identity::}>, {closure@issue-30786.rs:131:30}>`, but its trait bounds were not satisfied - --> $DIR/issue-30786.rs:132:24 - | -LL | pub struct Filter { - | ----------------------- method `countx` not found for this struct because it doesn't satisfy `_: StreamExt` -... -LL | let count = filter.countx(); - | ^^^^^^ method cannot be called due to unsatisfied trait bounds - | -note: the following trait bounds were not satisfied: - `&'a mut &Filter fn(&'a u64) -> &'a u64 {identity::}>, {closure@$DIR/issue-30786.rs:131:30: 131:37}>: Stream` - `&'a mut &mut Filter fn(&'a u64) -> &'a u64 {identity::}>, {closure@$DIR/issue-30786.rs:131:30: 131:37}>: Stream` - `&'a mut Filter fn(&'a u64) -> &'a u64 {identity::}>, {closure@$DIR/issue-30786.rs:131:30: 131:37}>: Stream` - --> $DIR/issue-30786.rs:98:50 - | -LL | impl StreamExt for T where for<'a> &'a mut T: Stream {} - | --------- - ^^^^^^ unsatisfied trait bound introduced here - = help: items from traits can only be used if the trait is implemented and in scope -note: `StreamExt` defines an item `countx`, perhaps you need to implement it - --> $DIR/issue-30786.rs:66:1 - | -LL | pub trait StreamExt - | ^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/impl-trait/in-trait/span-bug-issue-121457.rs b/tests/ui/impl-trait/in-trait/span-bug-issue-121457.rs index ab21dae7dc50d..7a51037324f28 100644 --- a/tests/ui/impl-trait/in-trait/span-bug-issue-121457.rs +++ b/tests/ui/impl-trait/in-trait/span-bug-issue-121457.rs @@ -14,6 +14,7 @@ impl<'a, I: 'a + Iterable> Iterable for &'a I { //~^ ERROR binding for associated type `Item` references lifetime `'missing` //~| ERROR binding for associated type `Item` references lifetime `'missing` //~| ERROR `()` is not an iterator + //~| WARNING impl trait in impl method signature does not match trait method signature } fn main() {} diff --git a/tests/ui/impl-trait/in-trait/span-bug-issue-121457.stderr b/tests/ui/impl-trait/in-trait/span-bug-issue-121457.stderr index d8a2eef94a180..67c4df0f3a9eb 100644 --- a/tests/ui/impl-trait/in-trait/span-bug-issue-121457.stderr +++ b/tests/ui/impl-trait/in-trait/span-bug-issue-121457.stderr @@ -32,7 +32,24 @@ LL | fn iter(&self) -> impl for<'missing> Iterator $DIR/span-bug-issue-121457.rs:13:51 + | +LL | fn iter(&self) -> impl Iterator; + | ------------- return type from trait method defined here +... +LL | fn iter(&self) -> impl for<'missing> Iterator> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ this bound is stronger than that defined on the trait + | + = note: add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate + = note: we are soliciting feedback, see issue #121718 for more information + = note: `#[warn(refining_impl_trait_reachable)]` on by default +help: replace the return type so that it matches the trait + | +LL | fn iter(&self) -> impl Iterator {} + | ~~~~~~~~~~~~~ + +error: aborting due to 4 previous errors; 1 warning emitted Some errors have detailed explanations: E0195, E0277, E0582. For more information about an error, try `rustc --explain E0195`. diff --git a/tests/ui/impl-trait/in-trait/synthetic-hir-has-parent.rs b/tests/ui/impl-trait/in-trait/synthetic-hir-has-parent.rs new file mode 100644 index 0000000000000..0e07d21b2f5bc --- /dev/null +++ b/tests/ui/impl-trait/in-trait/synthetic-hir-has-parent.rs @@ -0,0 +1,11 @@ +// Don't panic when iterating through the `hir::Map::parent_iter` of an RPITIT. + +pub trait Foo { + fn demo() -> impl Foo + //~^ ERROR the trait bound `String: Copy` is not satisfied + where + String: Copy; + //~^ ERROR the trait bound `String: Copy` is not satisfied +} + +fn main() {} diff --git a/tests/ui/impl-trait/in-trait/synthetic-hir-has-parent.stderr b/tests/ui/impl-trait/in-trait/synthetic-hir-has-parent.stderr new file mode 100644 index 0000000000000..8ff8f12cdf462 --- /dev/null +++ b/tests/ui/impl-trait/in-trait/synthetic-hir-has-parent.stderr @@ -0,0 +1,27 @@ +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/synthetic-hir-has-parent.rs:7:9 + | +LL | String: Copy; + | ^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` + | + = help: see issue #48214 +help: add `#![feature(trivial_bounds)]` to the crate attributes to enable + | +LL + #![feature(trivial_bounds)] + | + +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/synthetic-hir-has-parent.rs:4:18 + | +LL | fn demo() -> impl Foo + | ^^^^^^^^ the trait `Copy` is not implemented for `String` + | + = help: see issue #48214 +help: add `#![feature(trivial_bounds)]` to the crate attributes to enable + | +LL + #![feature(trivial_bounds)] + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/impl-trait/nested_impl_trait.rs b/tests/ui/impl-trait/nested_impl_trait.rs index 760102794c34e..502b2af2bc660 100644 --- a/tests/ui/impl-trait/nested_impl_trait.rs +++ b/tests/ui/impl-trait/nested_impl_trait.rs @@ -5,7 +5,7 @@ fn fine(x: impl Into) -> impl Into { x } fn bad_in_ret_position(x: impl Into) -> impl Into { x } //~^ ERROR nested `impl Trait` is not allowed -//~| ERROR the trait bound `impl Debug: From>` is not satisfied +//~| ERROR the trait bound `impl Into: Into` is not satisfied fn bad_in_fn_syntax(x: fn() -> impl Into) {} //~^ ERROR nested `impl Trait` is not allowed @@ -18,7 +18,7 @@ struct X; impl X { fn bad(x: impl Into) -> impl Into { x } //~^ ERROR nested `impl Trait` is not allowed - //~| ERROR the trait bound `impl Debug: From>` is not satisfied + //~| ERROR the trait bound `impl Into: Into` is not satisfied } fn allowed_in_assoc_type() -> impl Iterator { diff --git a/tests/ui/impl-trait/nested_impl_trait.stderr b/tests/ui/impl-trait/nested_impl_trait.stderr index 83d1347aff431..1f9a2a5e9d600 100644 --- a/tests/ui/impl-trait/nested_impl_trait.stderr +++ b/tests/ui/impl-trait/nested_impl_trait.stderr @@ -42,20 +42,20 @@ LL | fn bad_in_fn_syntax(x: fn() -> impl Into) {} | = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0277]: the trait bound `impl Debug: From>` is not satisfied +error[E0277]: the trait bound `impl Into: Into` is not satisfied --> $DIR/nested_impl_trait.rs:6:46 | LL | fn bad_in_ret_position(x: impl Into) -> impl Into { x } - | ^^^^^^^^^^^^^^^^^^^^^ the trait `From>` is not implemented for `impl Debug`, which is required by `impl Into: Into` + | ^^^^^^^^^^^^^^^^^^^^^ the trait `From>` is not implemented for `impl Into`, which is required by `impl Into: Into` | = help: the trait `Into` is implemented for `T` = note: required for `impl Into` to implement `Into` -error[E0277]: the trait bound `impl Debug: From>` is not satisfied +error[E0277]: the trait bound `impl Into: Into` is not satisfied --> $DIR/nested_impl_trait.rs:19:34 | LL | fn bad(x: impl Into) -> impl Into { x } - | ^^^^^^^^^^^^^^^^^^^^^ the trait `From>` is not implemented for `impl Debug`, which is required by `impl Into: Into` + | ^^^^^^^^^^^^^^^^^^^^^ the trait `From>` is not implemented for `impl Into`, which is required by `impl Into: Into` | = help: the trait `Into` is implemented for `T` = note: required for `impl Into` to implement `Into` diff --git a/tests/ui/impl-trait/recursive-coroutine-boxed.next.stderr b/tests/ui/impl-trait/recursive-coroutine-boxed.next.stderr index 755d12d744823..c0b399746eaf9 100644 --- a/tests/ui/impl-trait/recursive-coroutine-boxed.next.stderr +++ b/tests/ui/impl-trait/recursive-coroutine-boxed.next.stderr @@ -1,5 +1,5 @@ error[E0282]: type annotations needed - --> $DIR/recursive-coroutine-boxed.rs:12:23 + --> $DIR/recursive-coroutine-boxed.rs:14:23 | LL | let mut gen = Box::pin(foo()); | ^^^^^^^^ cannot infer type of the type parameter `T` declared on the struct `Box` @@ -12,12 +12,28 @@ help: consider specifying the generic argument LL | let mut gen = Box::::pin(foo()); | +++++ -error[E0282]: type annotations needed - --> $DIR/recursive-coroutine-boxed.rs:9:13 +error[E0308]: mismatched types + --> $DIR/recursive-coroutine-boxed.rs:13:5 + | +LL | fn foo() -> impl Coroutine { + | --------------------------------------- + | | + | the expected opaque type + | expected `impl Coroutine` because of return type +... +LL | / || { +LL | | let mut gen = Box::pin(foo()); +LL | | +LL | | let mut r = gen.as_mut().resume(()); +... | +LL | | } +LL | | } + | |_____^ types differ | -LL | fn foo() -> impl Coroutine { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for opaque type `impl Coroutine` + = note: expected opaque type `impl Coroutine` + found coroutine `{coroutine@$DIR/recursive-coroutine-boxed.rs:13:5: 13:7}` error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0282`. +Some errors have detailed explanations: E0282, E0308. +For more information about an error, try `rustc --explain E0282`. diff --git a/tests/ui/impl-trait/recursive-coroutine-boxed.rs b/tests/ui/impl-trait/recursive-coroutine-boxed.rs index 3b8ffb9209086..02c75be0f3aa0 100644 --- a/tests/ui/impl-trait/recursive-coroutine-boxed.rs +++ b/tests/ui/impl-trait/recursive-coroutine-boxed.rs @@ -7,8 +7,10 @@ use std::ops::{Coroutine, CoroutineState}; fn foo() -> impl Coroutine { - //[next]~^ ERROR type annotations needed - || { + // FIXME(-Znext-solver): this fails with a mismatched types as the + // hidden type of the opaque ends up as {type error}. We should not + // emit errors for such goals. + || { //[next]~ ERROR mismatched types let mut gen = Box::pin(foo()); //[next]~^ ERROR type annotations needed let mut r = gen.as_mut().resume(()); diff --git a/tests/ui/implied-bounds/issue-100690.rs b/tests/ui/implied-bounds/issue-100690.rs index ea33c9f423b77..041c687ec9430 100644 --- a/tests/ui/implied-bounds/issue-100690.rs +++ b/tests/ui/implied-bounds/issue-100690.rs @@ -4,11 +4,8 @@ use std::io; fn real_dispatch(f: F) -> Result<(), io::Error> -//~^ NOTE required by a bound in this where F: FnOnce(&mut UIView) -> Result<(), io::Error> + Send + 'static, - //~^ NOTE required by this bound in `real_dispatch` - //~| NOTE required by a bound in `real_dispatch` { todo!() } @@ -35,10 +32,10 @@ impl<'a, T: 'a> Handle<'a, T, UIView<'a, T>, Result<(), io::Error>> for TUIHandl F: FnOnce(&mut UIView<'a, T>) -> Result<(), io::Error> + Send + 'static, { real_dispatch(f) - //~^ ERROR expected a `FnOnce(&mut UIView<'_, T>)` closure, found `F` - //~| NOTE expected an `FnOnce(&mut UIView<'_, T>)` closure, found `F` - //~| NOTE expected a closure with arguments - //~| NOTE required by a bound introduced by this call + //~^ ERROR lifetime may not live long enough + //~| ERROR implementation of `FnOnce` is not general enough + //~| ERROR mismatched types + // } } diff --git a/tests/ui/implied-bounds/issue-100690.stderr b/tests/ui/implied-bounds/issue-100690.stderr index df069d875cebc..2cfd028f2559b 100644 --- a/tests/ui/implied-bounds/issue-100690.stderr +++ b/tests/ui/implied-bounds/issue-100690.stderr @@ -1,22 +1,41 @@ -error[E0277]: expected a `FnOnce(&mut UIView<'_, T>)` closure, found `F` - --> $DIR/issue-100690.rs:37:23 +error: lifetime may not live long enough + --> $DIR/issue-100690.rs:34:9 | +LL | impl<'a, T: 'a> Handle<'a, T, UIView<'a, T>, Result<(), io::Error>> for TUIHandle { + | -- lifetime `'a` defined here +... LL | real_dispatch(f) - | ------------- ^ expected an `FnOnce(&mut UIView<'_, T>)` closure, found `F` - | | - | required by a bound introduced by this call + | ^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` | - = note: expected a closure with arguments `(&mut UIView<'a, _>,)` - found a closure with arguments `(&mut UIView<'_, _>,)` -note: required by a bound in `real_dispatch` - --> $DIR/issue-100690.rs:9:8 +note: due to current limitations in the borrow checker, this implies a `'static` lifetime + --> $DIR/issue-100690.rs:8:8 + | +LL | F: FnOnce(&mut UIView) -> Result<(), io::Error> + Send + 'static, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: implementation of `FnOnce` is not general enough + --> $DIR/issue-100690.rs:34:9 + | +LL | real_dispatch(f) + | ^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough + | + = note: `F` must implement `FnOnce<(&mut UIView<'0, T>,)>`, for any lifetime `'0`... + = note: ...but it actually implements `FnOnce<(&mut UIView<'1, T>,)>`, for some specific lifetime `'1` + +error[E0308]: mismatched types + --> $DIR/issue-100690.rs:34:9 + | +LL | real_dispatch(f) + | ^^^^^^^^^^^^^^^^ one type is more general than the other + | + = note: expected associated type `,)>>::Output` + found associated type `,)>>::Output` +note: the lifetime requirement is introduced here + --> $DIR/issue-100690.rs:8:34 | -LL | fn real_dispatch(f: F) -> Result<(), io::Error> - | ------------- required by a bound in this function -... LL | F: FnOnce(&mut UIView) -> Result<(), io::Error> + Send + 'static, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `real_dispatch` + | ^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 1 previous error +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/issues/issue-1476.rs b/tests/ui/issues/issue-1476.rs deleted file mode 100644 index 138570a93c498..0000000000000 --- a/tests/ui/issues/issue-1476.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("{}", x); //~ ERROR cannot find value `x` in this scope -} diff --git a/tests/ui/issues/issue-1476.stderr b/tests/ui/issues/issue-1476.stderr deleted file mode 100644 index e30dbfd205b87..0000000000000 --- a/tests/ui/issues/issue-1476.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0425]: cannot find value `x` in this scope - --> $DIR/issue-1476.rs:2:20 - | -LL | println!("{}", x); - | ^ not found in this scope - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/issues/issue-1696.rs b/tests/ui/issues/issue-1696.rs deleted file mode 100644 index 08002ad3c58b1..0000000000000 --- a/tests/ui/issues/issue-1696.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ run-pass -use std::collections::HashMap; - -pub fn main() { - let mut m = HashMap::new(); - m.insert(b"foo".to_vec(), b"bar".to_vec()); - println!("{:?}", m); -} diff --git a/tests/ui/layout/ice-non-last-unsized-field-issue-121473.rs b/tests/ui/layout/ice-non-last-unsized-field-issue-121473.rs new file mode 100644 index 0000000000000..737a7ffbc296c --- /dev/null +++ b/tests/ui/layout/ice-non-last-unsized-field-issue-121473.rs @@ -0,0 +1,79 @@ +// Regression test for #121473 +// Checks that no ICE occurs when `size_of` +// is applied to a struct that has an unsized +// field which is not its last field + +use std::mem::size_of; + +pub struct BadStruct { + pub field1: i32, + pub field2: str, // Unsized field that is not the last field + //~^ ERROR the size for values of type `str` cannot be known at compilation time + pub field3: [u8; 16], +} + +enum BadEnum1 { + Variant1 { + field1: i32, + field2: str, // Unsized + //~^ ERROR the size for values of type `str` cannot be known at compilation time + field3: [u8; 16], + }, +} + +enum BadEnum2 { + Variant1( + i32, + str, // Unsized + //~^ ERROR the size for values of type `str` cannot be known at compilation time + [u8; 16] + ), +} + +enum BadEnumMultiVariant { + Variant1(i32), + Variant2 { + field1: i32, + field2: str, // Unsized + //~^ ERROR the size for values of type `str` cannot be known at compilation time + field3: [u8; 16], + }, + Variant3 +} + +union BadUnion { + field1: i32, + field2: str, // Unsized + //~^ ERROR the size for values of type `str` cannot be known at compilation time + //~| ERROR field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union + field3: [u8; 16], +} + +// Used to test that projection type fields that normalize +// to a sized type do not cause problems +struct StructWithProjections<'a> +{ + field1: <&'a [i32] as IntoIterator>::IntoIter, + field2: i32 +} + +pub fn main() { + let _a = &size_of::(); + assert_eq!(size_of::(), 21); + + let _a = &size_of::(); + assert_eq!(size_of::(), 21); + + let _a = &size_of::(); + assert_eq!(size_of::(), 21); + + let _a = &size_of::(); + assert_eq!(size_of::(), 21); + + let _a = &size_of::(); + assert_eq!(size_of::(), 21); + + let _a = &size_of::(); + assert_eq!(size_of::(), 21); + let _a = StructWithProjections { field1: [1, 3].iter(), field2: 3 }; +} diff --git a/tests/ui/layout/ice-non-last-unsized-field-issue-121473.stderr b/tests/ui/layout/ice-non-last-unsized-field-issue-121473.stderr new file mode 100644 index 0000000000000..626be7ac2836b --- /dev/null +++ b/tests/ui/layout/ice-non-last-unsized-field-issue-121473.stderr @@ -0,0 +1,106 @@ +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/ice-non-last-unsized-field-issue-121473.rs:10:17 + | +LL | pub field2: str, // Unsized field that is not the last field + | ^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `str` + = note: only the last field of a struct may have a dynamically sized type + = help: change the field's type to have a statically known size +help: borrowed types always have a statically known size + | +LL | pub field2: &str, // Unsized field that is not the last field + | + +help: the `Box` type always has a statically known size and allocates its contents in the heap + | +LL | pub field2: Box, // Unsized field that is not the last field + | ++++ + + +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/ice-non-last-unsized-field-issue-121473.rs:18:17 + | +LL | field2: str, // Unsized + | ^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `str` + = note: no field of an enum variant may have a dynamically sized type + = help: change the field's type to have a statically known size +help: borrowed types always have a statically known size + | +LL | field2: &str, // Unsized + | + +help: the `Box` type always has a statically known size and allocates its contents in the heap + | +LL | field2: Box, // Unsized + | ++++ + + +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/ice-non-last-unsized-field-issue-121473.rs:27:9 + | +LL | str, // Unsized + | ^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `str` + = note: no field of an enum variant may have a dynamically sized type + = help: change the field's type to have a statically known size +help: borrowed types always have a statically known size + | +LL | &str, // Unsized + | + +help: the `Box` type always has a statically known size and allocates its contents in the heap + | +LL | Box, // Unsized + | ++++ + + +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/ice-non-last-unsized-field-issue-121473.rs:37:17 + | +LL | field2: str, // Unsized + | ^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `str` + = note: no field of an enum variant may have a dynamically sized type + = help: change the field's type to have a statically known size +help: borrowed types always have a statically known size + | +LL | field2: &str, // Unsized + | + +help: the `Box` type always has a statically known size and allocates its contents in the heap + | +LL | field2: Box, // Unsized + | ++++ + + +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/ice-non-last-unsized-field-issue-121473.rs:46:13 + | +LL | field2: str, // Unsized + | ^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `str` + = note: no field of a union may have a dynamically sized type + = help: change the field's type to have a statically known size +help: borrowed types always have a statically known size + | +LL | field2: &str, // Unsized + | + +help: the `Box` type always has a statically known size and allocates its contents in the heap + | +LL | field2: Box, // Unsized + | ++++ + + +error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union + --> $DIR/ice-non-last-unsized-field-issue-121473.rs:46:5 + | +LL | field2: str, // Unsized + | ^^^^^^^^^^^ + | + = note: union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>` +help: wrap the field type in `ManuallyDrop<...>` + | +LL | field2: std::mem::ManuallyDrop, // Unsized + | +++++++++++++++++++++++ + + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0277, E0740. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/lifetimes/issue-105675.rs b/tests/ui/lifetimes/issue-105675.rs index 2e2eaca0d335c..0472537e7f34c 100644 --- a/tests/ui/lifetimes/issue-105675.rs +++ b/tests/ui/lifetimes/issue-105675.rs @@ -4,7 +4,7 @@ fn main() { let f = | _ , y: &u32 , z | (); thing(f); //~^ ERROR implementation of `FnOnce` is not general enough - //~^^ ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough let f = | x, y: _ , z: u32 | (); thing(f); //~^ ERROR implementation of `FnOnce` is not general enough diff --git a/tests/ui/lifetimes/lifetime-errors/issue_74400.rs b/tests/ui/lifetimes/lifetime-errors/issue_74400.rs index b02e38bec3b3e..72345fa294a43 100644 --- a/tests/ui/lifetimes/lifetime-errors/issue_74400.rs +++ b/tests/ui/lifetimes/lifetime-errors/issue_74400.rs @@ -1,5 +1,5 @@ //! Regression test for #74400: Type mismatch in function arguments E0631, E0271 are falsely -//! recognized as E0308 mismatched types. +//! recognized as "implementation of `FnOnce` is not general enough". use std::convert::identity; @@ -13,6 +13,6 @@ fn g(data: &[T]) { //~^ ERROR the parameter type //~| ERROR the parameter type //~| ERROR the parameter type - //~| ERROR implementation of `FnOnce` is not general + //~| ERROR implementation of `FnOnce` is not general enough //~| ERROR implementation of `Fn` is not general enough } diff --git a/tests/ui/lint/lint-strict-provenance-fuzzy-casts.stderr b/tests/ui/lint/lint-strict-provenance-fuzzy-casts.stderr index 0b12897427586..24f2500abf82c 100644 --- a/tests/ui/lint/lint-strict-provenance-fuzzy-casts.stderr +++ b/tests/ui/lint/lint-strict-provenance-fuzzy-casts.stderr @@ -4,7 +4,7 @@ error: strict provenance disallows casting integer `usize` to pointer `*const u8 LL | let dangling = 16_usize as *const u8; | ^^^^^^^^^^^^^^^^^^^^^ | - = help: if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::from_exposed_addr()` instead + = help: if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead note: the lint level is defined here --> $DIR/lint-strict-provenance-fuzzy-casts.rs:2:9 | diff --git a/tests/ui/lint/lint-strict-provenance-lossy-casts.stderr b/tests/ui/lint/lint-strict-provenance-lossy-casts.stderr index aa151fe2d214e..390028b349e5f 100644 --- a/tests/ui/lint/lint-strict-provenance-lossy-casts.stderr +++ b/tests/ui/lint/lint-strict-provenance-lossy-casts.stderr @@ -4,7 +4,7 @@ error: under strict provenance it is considered bad style to cast pointer `*cons LL | let addr: usize = &x as *const u8 as usize; | ^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_addr()` instead + = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead note: the lint level is defined here --> $DIR/lint-strict-provenance-lossy-casts.rs:2:9 | @@ -21,7 +21,7 @@ error: under strict provenance it is considered bad style to cast pointer `*cons LL | let addr_32bit = &x as *const u8 as u32; | ^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_addr()` instead + = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead help: use `.addr()` to obtain the address of a pointer | LL | let addr_32bit = (&x as *const u8).addr() as u32; @@ -35,7 +35,7 @@ LL | let ptr_addr = ptr as usize; | | | help: use `.addr()` to obtain the address of a pointer: `.addr()` | - = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_addr()` instead + = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `u32` --> $DIR/lint-strict-provenance-lossy-casts.rs:16:26 @@ -45,7 +45,7 @@ LL | let ptr_addr_32bit = ptr as u32; | | | help: use `.addr()` to obtain the address of a pointer: `.addr() as u32` | - = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_addr()` instead + = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead error: aborting due to 4 previous errors diff --git a/tests/ui/lint/wide_pointer_comparisons.rs b/tests/ui/lint/wide_pointer_comparisons.rs index 313690010754e..05097cbf1e3de 100644 --- a/tests/ui/lint/wide_pointer_comparisons.rs +++ b/tests/ui/lint/wide_pointer_comparisons.rs @@ -3,6 +3,7 @@ use std::rc::Rc; use std::sync::Arc; use std::cmp::PartialEq; +use std::ptr::NonNull; struct A; struct B; @@ -37,6 +38,29 @@ fn main() { //~^ WARN ambiguous wide pointer comparison let _ = a.ne(&b); //~^ WARN ambiguous wide pointer comparison + let _ = a.cmp(&b); + //~^ WARN ambiguous wide pointer comparison + let _ = a.partial_cmp(&b); + //~^ WARN ambiguous wide pointer comparison + let _ = a.le(&b); + //~^ WARN ambiguous wide pointer comparison + let _ = a.lt(&b); + //~^ WARN ambiguous wide pointer comparison + let _ = a.ge(&b); + //~^ WARN ambiguous wide pointer comparison + let _ = a.gt(&b); + //~^ WARN ambiguous wide pointer comparison + + { + let a = NonNull::::new(a as *mut _).unwrap(); + let b = NonNull::::new(b as *mut _).unwrap(); + let _ = a == b; + //~^ WARN ambiguous wide pointer comparison + let _ = a >= b; + //~^ WARN ambiguous wide pointer comparison + let _ = &a == &b; + //~^ WARN ambiguous wide pointer comparison + } { // &*const ?Sized @@ -68,6 +92,18 @@ fn main() { //~^ WARN ambiguous wide pointer comparison let _ = a.ne(b); //~^ WARN ambiguous wide pointer comparison + let _ = a.cmp(&b); + //~^ WARN ambiguous wide pointer comparison + let _ = a.partial_cmp(&b); + //~^ WARN ambiguous wide pointer comparison + let _ = a.le(&b); + //~^ WARN ambiguous wide pointer comparison + let _ = a.lt(&b); + //~^ WARN ambiguous wide pointer comparison + let _ = a.ge(&b); + //~^ WARN ambiguous wide pointer comparison + let _ = a.gt(&b); + //~^ WARN ambiguous wide pointer comparison } let s = "" as *const str; diff --git a/tests/ui/lint/wide_pointer_comparisons.stderr b/tests/ui/lint/wide_pointer_comparisons.stderr index 6ef117c63c5b8..81a221c0ee640 100644 --- a/tests/ui/lint/wide_pointer_comparisons.stderr +++ b/tests/ui/lint/wide_pointer_comparisons.stderr @@ -1,5 +1,5 @@ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:19:13 + --> $DIR/wide_pointer_comparisons.rs:20:13 | LL | let _ = a == b; | ^^^^^^ @@ -11,7 +11,7 @@ LL | let _ = std::ptr::addr_eq(a, b); | ++++++++++++++++++ ~ + warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:21:13 + --> $DIR/wide_pointer_comparisons.rs:22:13 | LL | let _ = a != b; | ^^^^^^ @@ -22,51 +22,51 @@ LL | let _ = !std::ptr::addr_eq(a, b); | +++++++++++++++++++ ~ + warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:23:13 + --> $DIR/wide_pointer_comparisons.rs:24:13 | LL | let _ = a < b; | ^^^^^ | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = a as *const () < b as *const (); - | ++++++++++++ ++++++++++++ +LL | let _ = a.cast::<()>() < b.cast::<()>(); + | +++++++++++++ +++++++++++++ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:25:13 + --> $DIR/wide_pointer_comparisons.rs:26:13 | LL | let _ = a <= b; | ^^^^^^ | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = a as *const () <= b as *const (); - | ++++++++++++ ++++++++++++ +LL | let _ = a.cast::<()>() <= b.cast::<()>(); + | +++++++++++++ +++++++++++++ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:27:13 + --> $DIR/wide_pointer_comparisons.rs:28:13 | LL | let _ = a > b; | ^^^^^ | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = a as *const () > b as *const (); - | ++++++++++++ ++++++++++++ +LL | let _ = a.cast::<()>() > b.cast::<()>(); + | +++++++++++++ +++++++++++++ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:29:13 + --> $DIR/wide_pointer_comparisons.rs:30:13 | LL | let _ = a >= b; | ^^^^^^ | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = a as *const () >= b as *const (); - | ++++++++++++ ++++++++++++ +LL | let _ = a.cast::<()>() >= b.cast::<()>(); + | +++++++++++++ +++++++++++++ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:32:13 + --> $DIR/wide_pointer_comparisons.rs:33:13 | LL | let _ = PartialEq::eq(&a, &b); | ^^^^^^^^^^^^^^^^^^^^^ @@ -77,7 +77,7 @@ LL | let _ = std::ptr::addr_eq(a, b); | ~~~~~~~~~~~~~~~~~~ ~ ~ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:34:13 + --> $DIR/wide_pointer_comparisons.rs:35:13 | LL | let _ = PartialEq::ne(&a, &b); | ^^^^^^^^^^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | let _ = !std::ptr::addr_eq(a, b); | ~~~~~~~~~~~~~~~~~~~ ~ ~ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:36:13 + --> $DIR/wide_pointer_comparisons.rs:37:13 | LL | let _ = a.eq(&b); | ^^^^^^^^ @@ -99,7 +99,7 @@ LL | let _ = std::ptr::addr_eq(a, b); | ++++++++++++++++++ ~ ~ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:38:13 + --> $DIR/wide_pointer_comparisons.rs:39:13 | LL | let _ = a.ne(&b); | ^^^^^^^^ @@ -110,7 +110,106 @@ LL | let _ = !std::ptr::addr_eq(a, b); | +++++++++++++++++++ ~ ~ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:46:17 + --> $DIR/wide_pointer_comparisons.rs:41:13 + | +LL | let _ = a.cmp(&b); + | ^^^^^^^^^ + | +help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses + | +LL | let _ = a.cast::<()>().cmp(&b.cast::<()>()); + | +++++++++++++ +++++++++++++ + +warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected + --> $DIR/wide_pointer_comparisons.rs:43:13 + | +LL | let _ = a.partial_cmp(&b); + | ^^^^^^^^^^^^^^^^^ + | +help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses + | +LL | let _ = a.cast::<()>().partial_cmp(&b.cast::<()>()); + | +++++++++++++ +++++++++++++ + +warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected + --> $DIR/wide_pointer_comparisons.rs:45:13 + | +LL | let _ = a.le(&b); + | ^^^^^^^^ + | +help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses + | +LL | let _ = a.cast::<()>().le(&b.cast::<()>()); + | +++++++++++++ +++++++++++++ + +warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected + --> $DIR/wide_pointer_comparisons.rs:47:13 + | +LL | let _ = a.lt(&b); + | ^^^^^^^^ + | +help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses + | +LL | let _ = a.cast::<()>().lt(&b.cast::<()>()); + | +++++++++++++ +++++++++++++ + +warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected + --> $DIR/wide_pointer_comparisons.rs:49:13 + | +LL | let _ = a.ge(&b); + | ^^^^^^^^ + | +help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses + | +LL | let _ = a.cast::<()>().ge(&b.cast::<()>()); + | +++++++++++++ +++++++++++++ + +warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected + --> $DIR/wide_pointer_comparisons.rs:51:13 + | +LL | let _ = a.gt(&b); + | ^^^^^^^^ + | +help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses + | +LL | let _ = a.cast::<()>().gt(&b.cast::<()>()); + | +++++++++++++ +++++++++++++ + +warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected + --> $DIR/wide_pointer_comparisons.rs:57:17 + | +LL | let _ = a == b; + | ^^^^^^ + | +help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses + | +LL | let _ = std::ptr::addr_eq(a.as_ptr(), b.as_ptr()); + | ++++++++++++++++++ ~~~~~~~~~~ ++++++++++ + +warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected + --> $DIR/wide_pointer_comparisons.rs:59:17 + | +LL | let _ = a >= b; + | ^^^^^^ + | +help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses + | +LL | let _ = a.as_ptr().cast::<()>() >= b.as_ptr().cast::<()>(); + | ++++++++++++++++++++++ ++++++++++++++++++++++ + +warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected + --> $DIR/wide_pointer_comparisons.rs:61:17 + | +LL | let _ = &a == &b; + | ^^^^^^^^ + | +help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses + | +LL | let _ = std::ptr::addr_eq(a.as_ptr(), b.as_ptr()); + | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~ ++++++++++ + +warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected + --> $DIR/wide_pointer_comparisons.rs:70:17 | LL | let _ = a == b; | ^^^^^^ @@ -121,7 +220,7 @@ LL | let _ = std::ptr::addr_eq(*a, *b); | +++++++++++++++++++ ~~~ + warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:48:17 + --> $DIR/wide_pointer_comparisons.rs:72:17 | LL | let _ = a != b; | ^^^^^^ @@ -132,51 +231,51 @@ LL | let _ = !std::ptr::addr_eq(*a, *b); | ++++++++++++++++++++ ~~~ + warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:50:17 + --> $DIR/wide_pointer_comparisons.rs:74:17 | LL | let _ = a < b; | ^^^^^ | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = *a as *const () < *b as *const (); - | + ++++++++++++ + ++++++++++++ +LL | let _ = (*a).cast::<()>() < (*b).cast::<()>(); + | ++ ++++++++++++++ ++ ++++++++++++++ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:52:17 + --> $DIR/wide_pointer_comparisons.rs:76:17 | LL | let _ = a <= b; | ^^^^^^ | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = *a as *const () <= *b as *const (); - | + ++++++++++++ + ++++++++++++ +LL | let _ = (*a).cast::<()>() <= (*b).cast::<()>(); + | ++ ++++++++++++++ ++ ++++++++++++++ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:54:17 + --> $DIR/wide_pointer_comparisons.rs:78:17 | LL | let _ = a > b; | ^^^^^ | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = *a as *const () > *b as *const (); - | + ++++++++++++ + ++++++++++++ +LL | let _ = (*a).cast::<()>() > (*b).cast::<()>(); + | ++ ++++++++++++++ ++ ++++++++++++++ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:56:17 + --> $DIR/wide_pointer_comparisons.rs:80:17 | LL | let _ = a >= b; | ^^^^^^ | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = *a as *const () >= *b as *const (); - | + ++++++++++++ + ++++++++++++ +LL | let _ = (*a).cast::<()>() >= (*b).cast::<()>(); + | ++ ++++++++++++++ ++ ++++++++++++++ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:59:17 + --> $DIR/wide_pointer_comparisons.rs:83:17 | LL | let _ = PartialEq::eq(a, b); | ^^^^^^^^^^^^^^^^^^^ @@ -187,7 +286,7 @@ LL | let _ = std::ptr::addr_eq(*a, *b); | ~~~~~~~~~~~~~~~~~~~ ~~~ ~ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:61:17 + --> $DIR/wide_pointer_comparisons.rs:85:17 | LL | let _ = PartialEq::ne(a, b); | ^^^^^^^^^^^^^^^^^^^ @@ -198,7 +297,7 @@ LL | let _ = !std::ptr::addr_eq(*a, *b); | ~~~~~~~~~~~~~~~~~~~~ ~~~ ~ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:63:17 + --> $DIR/wide_pointer_comparisons.rs:87:17 | LL | let _ = PartialEq::eq(&a, &b); | ^^^^^^^^^^^^^^^^^^^^^ @@ -209,7 +308,7 @@ LL | let _ = std::ptr::addr_eq(*a, *b); | ~~~~~~~~~~~~~~~~~~~ ~~~ ~ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:65:17 + --> $DIR/wide_pointer_comparisons.rs:89:17 | LL | let _ = PartialEq::ne(&a, &b); | ^^^^^^^^^^^^^^^^^^^^^ @@ -220,7 +319,7 @@ LL | let _ = !std::ptr::addr_eq(*a, *b); | ~~~~~~~~~~~~~~~~~~~~ ~~~ ~ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:67:17 + --> $DIR/wide_pointer_comparisons.rs:91:17 | LL | let _ = a.eq(b); | ^^^^^^^ @@ -231,7 +330,7 @@ LL | let _ = std::ptr::addr_eq(*a, *b); | +++++++++++++++++++ ~~~ ~ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:69:17 + --> $DIR/wide_pointer_comparisons.rs:93:17 | LL | let _ = a.ne(b); | ^^^^^^^ @@ -242,7 +341,73 @@ LL | let _ = !std::ptr::addr_eq(*a, *b); | ++++++++++++++++++++ ~~~ ~ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:74:13 + --> $DIR/wide_pointer_comparisons.rs:95:17 + | +LL | let _ = a.cmp(&b); + | ^^^^^^^^^ + | +help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses + | +LL | let _ = (*a).cast::<()>().cmp(&(*b).cast::<()>()); + | ++ ++++++++++++++ ++ ++++++++++++++ + +warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected + --> $DIR/wide_pointer_comparisons.rs:97:17 + | +LL | let _ = a.partial_cmp(&b); + | ^^^^^^^^^^^^^^^^^ + | +help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses + | +LL | let _ = (*a).cast::<()>().partial_cmp(&(*b).cast::<()>()); + | ++ ++++++++++++++ ++ ++++++++++++++ + +warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected + --> $DIR/wide_pointer_comparisons.rs:99:17 + | +LL | let _ = a.le(&b); + | ^^^^^^^^ + | +help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses + | +LL | let _ = (*a).cast::<()>().le(&(*b).cast::<()>()); + | ++ ++++++++++++++ ++ ++++++++++++++ + +warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected + --> $DIR/wide_pointer_comparisons.rs:101:17 + | +LL | let _ = a.lt(&b); + | ^^^^^^^^ + | +help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses + | +LL | let _ = (*a).cast::<()>().lt(&(*b).cast::<()>()); + | ++ ++++++++++++++ ++ ++++++++++++++ + +warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected + --> $DIR/wide_pointer_comparisons.rs:103:17 + | +LL | let _ = a.ge(&b); + | ^^^^^^^^ + | +help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses + | +LL | let _ = (*a).cast::<()>().ge(&(*b).cast::<()>()); + | ++ ++++++++++++++ ++ ++++++++++++++ + +warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected + --> $DIR/wide_pointer_comparisons.rs:105:17 + | +LL | let _ = a.gt(&b); + | ^^^^^^^^ + | +help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses + | +LL | let _ = (*a).cast::<()>().gt(&(*b).cast::<()>()); + | ++ ++++++++++++++ ++ ++++++++++++++ + +warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected + --> $DIR/wide_pointer_comparisons.rs:110:13 | LL | let _ = s == s; | ^^^^^^ @@ -257,7 +422,7 @@ LL | let _ = std::ptr::eq(s, s); | +++++++++++++ ~ + warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:78:13 + --> $DIR/wide_pointer_comparisons.rs:114:13 | LL | let _ = s == s; | ^^^^^^ @@ -272,7 +437,7 @@ LL | let _ = std::ptr::eq(s, s); | +++++++++++++ ~ + warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:82:17 + --> $DIR/wide_pointer_comparisons.rs:118:17 | LL | let _ = a == b; | ^^^^^^ @@ -287,7 +452,7 @@ LL | let _ = std::ptr::eq(a, b); | +++++++++++++ ~ + warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:84:17 + --> $DIR/wide_pointer_comparisons.rs:120:17 | LL | let _ = a != b; | ^^^^^^ @@ -302,51 +467,51 @@ LL | let _ = !std::ptr::eq(a, b); | ++++++++++++++ ~ + warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:86:17 + --> $DIR/wide_pointer_comparisons.rs:122:17 | LL | let _ = a < b; | ^^^^^ | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = a as *const () < b as *const (); - | ++++++++++++ ++++++++++++ +LL | let _ = a.cast::<()>() < b.cast::<()>(); + | +++++++++++++ +++++++++++++ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:88:17 + --> $DIR/wide_pointer_comparisons.rs:124:17 | LL | let _ = a <= b; | ^^^^^^ | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = a as *const () <= b as *const (); - | ++++++++++++ ++++++++++++ +LL | let _ = a.cast::<()>() <= b.cast::<()>(); + | +++++++++++++ +++++++++++++ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:90:17 + --> $DIR/wide_pointer_comparisons.rs:126:17 | LL | let _ = a > b; | ^^^^^ | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = a as *const () > b as *const (); - | ++++++++++++ ++++++++++++ +LL | let _ = a.cast::<()>() > b.cast::<()>(); + | +++++++++++++ +++++++++++++ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:92:17 + --> $DIR/wide_pointer_comparisons.rs:128:17 | LL | let _ = a >= b; | ^^^^^^ | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = a as *const () >= b as *const (); - | ++++++++++++ ++++++++++++ +LL | let _ = a.cast::<()>() >= b.cast::<()>(); + | +++++++++++++ +++++++++++++ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:95:17 + --> $DIR/wide_pointer_comparisons.rs:131:17 | LL | let _ = PartialEq::eq(&a, &b); | ^^^^^^^^^^^^^^^^^^^^^ @@ -361,7 +526,7 @@ LL | let _ = std::ptr::eq(a, b); | ~~~~~~~~~~~~~ ~ ~ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:97:17 + --> $DIR/wide_pointer_comparisons.rs:133:17 | LL | let _ = PartialEq::ne(&a, &b); | ^^^^^^^^^^^^^^^^^^^^^ @@ -376,7 +541,7 @@ LL | let _ = !std::ptr::eq(a, b); | ~~~~~~~~~~~~~~ ~ ~ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:99:17 + --> $DIR/wide_pointer_comparisons.rs:135:17 | LL | let _ = a.eq(&b); | ^^^^^^^^ @@ -391,7 +556,7 @@ LL | let _ = std::ptr::eq(a, b); | +++++++++++++ ~ ~ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:101:17 + --> $DIR/wide_pointer_comparisons.rs:137:17 | LL | let _ = a.ne(&b); | ^^^^^^^^ @@ -406,7 +571,7 @@ LL | let _ = !std::ptr::eq(a, b); | ++++++++++++++ ~ ~ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:106:9 + --> $DIR/wide_pointer_comparisons.rs:142:9 | LL | &*a == &*b | ^^^^^^^^^^ @@ -421,7 +586,7 @@ LL | std::ptr::eq(*a, *b) | ~~~~~~~~~~~~~ ~ + warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:117:14 + --> $DIR/wide_pointer_comparisons.rs:153:14 | LL | cmp!(a, b); | ^^^^ @@ -432,7 +597,7 @@ LL | cmp!(std::ptr::addr_eq(a, b)); | ++++++++++++++++++ ~ + warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:123:39 + --> $DIR/wide_pointer_comparisons.rs:159:39 | LL | ($a:ident, $b:ident) => { $a == $b } | ^^^^^^^^ @@ -447,7 +612,7 @@ LL | ($a:ident, $b:ident) => { std::ptr::addr_eq($a, $b) } | ++++++++++++++++++ ~ + warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected - --> $DIR/wide_pointer_comparisons.rs:133:37 + --> $DIR/wide_pointer_comparisons.rs:169:37 | LL | ($a:expr, $b:expr) => { $a == $b } | ^^ @@ -459,5 +624,5 @@ LL | cmp!(&a, &b); = help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses = note: this warning originates in the macro `cmp` (in Nightly builds, run with -Z macro-backtrace for more info) -warning: 38 warnings emitted +warning: 53 warnings emitted diff --git a/tests/ui/issues/issue-1962.fixed b/tests/ui/loops/issue-1962.fixed similarity index 100% rename from tests/ui/issues/issue-1962.fixed rename to tests/ui/loops/issue-1962.fixed diff --git a/tests/ui/issues/issue-1962.rs b/tests/ui/loops/issue-1962.rs similarity index 100% rename from tests/ui/issues/issue-1962.rs rename to tests/ui/loops/issue-1962.rs diff --git a/tests/ui/issues/issue-1962.stderr b/tests/ui/loops/issue-1962.stderr similarity index 100% rename from tests/ui/issues/issue-1962.stderr rename to tests/ui/loops/issue-1962.stderr diff --git a/tests/ui/issues/issue-1974.rs b/tests/ui/loops/issue-1974.rs similarity index 100% rename from tests/ui/issues/issue-1974.rs rename to tests/ui/loops/issue-1974.rs diff --git a/tests/ui/marker_trait_attr/unsound-overlap.rs b/tests/ui/marker_trait_attr/unsound-overlap.rs index 2e5101b822c0c..2ce26b610f65d 100644 --- a/tests/ui/marker_trait_attr/unsound-overlap.rs +++ b/tests/ui/marker_trait_attr/unsound-overlap.rs @@ -8,6 +8,7 @@ trait B {} impl B for T {} impl A for T {} impl A for &str {} +//~^ ERROR type annotations needed: cannot satisfy `&str: A` impl A for (T,) {} trait TraitWithAssoc { type Assoc; diff --git a/tests/ui/marker_trait_attr/unsound-overlap.stderr b/tests/ui/marker_trait_attr/unsound-overlap.stderr index 5e58f5227ed61..13498fa4b2202 100644 --- a/tests/ui/marker_trait_attr/unsound-overlap.stderr +++ b/tests/ui/marker_trait_attr/unsound-overlap.stderr @@ -1,5 +1,19 @@ +error[E0283]: type annotations needed: cannot satisfy `&str: A` + --> $DIR/unsound-overlap.rs:10:12 + | +LL | impl A for &str {} + | ^^^^ + | +note: multiple `impl`s satisfying `&str: A` found + --> $DIR/unsound-overlap.rs:9:1 + | +LL | impl A for T {} + | ^^^^^^^^^^^^^^^^^^ +LL | impl A for &str {} + | ^^^^^^^^^^^^^^^ + error[E0119]: conflicting implementations of trait `TraitWithAssoc` for type `((&str,),)` - --> $DIR/unsound-overlap.rs:20:1 + --> $DIR/unsound-overlap.rs:21:1 | LL | impl TraitWithAssoc for T { | ------------------------------- first implementation here @@ -7,6 +21,7 @@ LL | impl TraitWithAssoc for T { LL | impl TraitWithAssoc for ((&str,),) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `((&str,),)` -error: aborting due to 1 previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0119`. +Some errors have detailed explanations: E0119, E0283. +For more information about an error, try `rustc --explain E0119`. diff --git a/tests/ui/match/postfix-match/match-after-as.rs b/tests/ui/match/postfix-match/match-after-as.rs new file mode 100644 index 0000000000000..7c648bfcf0353 --- /dev/null +++ b/tests/ui/match/postfix-match/match-after-as.rs @@ -0,0 +1,7 @@ +#![feature(postfix_match)] + +fn main() { + 1 as i32.match {}; + //~^ ERROR cast cannot be followed by a postfix match + //~| ERROR non-exhaustive patterns +} diff --git a/tests/ui/match/postfix-match/match-after-as.stderr b/tests/ui/match/postfix-match/match-after-as.stderr new file mode 100644 index 0000000000000..68e4762b8d91d --- /dev/null +++ b/tests/ui/match/postfix-match/match-after-as.stderr @@ -0,0 +1,28 @@ +error: cast cannot be followed by a postfix match + --> $DIR/match-after-as.rs:4:5 + | +LL | 1 as i32.match {}; + | ^^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | (1 as i32).match {}; + | + + + +error[E0004]: non-exhaustive patterns: type `i32` is non-empty + --> $DIR/match-after-as.rs:4:5 + | +LL | 1 as i32.match {}; + | ^^^^^^^^ + | + = note: the matched value is of type `i32` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown + | +LL ~ 1 as i32.match { +LL + _ => todo!(), +LL ~ }; + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/match/ref_pat_everywhere-mutability-mismatch.rs b/tests/ui/match/ref_pat_everywhere-mutability-mismatch.rs new file mode 100644 index 0000000000000..9dd7a7893ec71 --- /dev/null +++ b/tests/ui/match/ref_pat_everywhere-mutability-mismatch.rs @@ -0,0 +1,16 @@ +#![allow(incomplete_features)] +#![feature(ref_pat_everywhere)] +pub fn main() { + if let Some(&x) = Some(0) { + //~^ ERROR: mismatched types [E0308] + let _: u32 = x; + } + if let &Some(x) = &mut Some(0) { + //~^ ERROR: mismatched types [E0308] + let _: u32 = x; + } + if let Some(&x) = &mut Some(0) { + //~^ ERROR: mismatched types [E0308] + let _: u32 = x; + } +} diff --git a/tests/ui/match/ref_pat_everywhere-mutability-mismatch.stderr b/tests/ui/match/ref_pat_everywhere-mutability-mismatch.stderr new file mode 100644 index 0000000000000..d512ea5f957da --- /dev/null +++ b/tests/ui/match/ref_pat_everywhere-mutability-mismatch.stderr @@ -0,0 +1,44 @@ +error[E0308]: mismatched types + --> $DIR/ref_pat_everywhere-mutability-mismatch.rs:4:17 + | +LL | if let Some(&x) = Some(0) { + | ^^ ------- this expression has type `Option<{integer}>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL | if let Some(x) = Some(0) { + | ~ + +error[E0308]: mismatched types + --> $DIR/ref_pat_everywhere-mutability-mismatch.rs:8:12 + | +LL | if let &Some(x) = &mut Some(0) { + | ^^^^^^^^ ------------ this expression has type `&mut Option<{integer}>` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut Option<{integer}>` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/ref_pat_everywhere-mutability-mismatch.rs:12:17 + | +LL | if let Some(&x) = &mut Some(0) { + | ^^ ------------ this expression has type `&mut Option<{integer}>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL | if let Some(x) = &mut Some(0) { + | ~ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/match/ref_pat_everywhere.rs b/tests/ui/match/ref_pat_everywhere.rs new file mode 100644 index 0000000000000..b3daca484092b --- /dev/null +++ b/tests/ui/match/ref_pat_everywhere.rs @@ -0,0 +1,18 @@ +//@ run-pass +#![allow(incomplete_features)] +#![feature(ref_pat_everywhere)] + +pub fn main() { + if let Some(Some(&x)) = &Some(&Some(0)) { + let _: u32 = x; + } + if let Some(&Some(x)) = &Some(Some(0)) { + let _: u32 = x; + } + if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { + let _: u32 = x; + } + if let Some(Some(&x)) = &Some(&mut Some(0)) { + let _: u32 = x; + } +} diff --git a/tests/ui/methods/opaque_param_in_ufc.rs b/tests/ui/methods/opaque_param_in_ufc.rs new file mode 100644 index 0000000000000..a4b27a0131fd0 --- /dev/null +++ b/tests/ui/methods/opaque_param_in_ufc.rs @@ -0,0 +1,30 @@ +#![feature(type_alias_impl_trait)] +struct Foo(T); + +impl Foo { + fn method() {} + fn method2(self) {} +} + +type Bar = impl Sized; + +fn bar() -> Bar { + 42_u32 +} + +impl Foo { + fn foo() -> Bar { + Self::method(); + //~^ ERROR: no function or associated item named `method` found for struct `Foo` + Foo::::method(); + //~^ ERROR: no function or associated item named `method` found for struct `Foo` + let x = Foo(bar()); + Foo::method2(x); + let x = Self(bar()); + Self::method2(x); + //~^ ERROR: no function or associated item named `method2` found for struct `Foo` + todo!() + } +} + +fn main() {} diff --git a/tests/ui/methods/opaque_param_in_ufc.stderr b/tests/ui/methods/opaque_param_in_ufc.stderr new file mode 100644 index 0000000000000..7e5bbbac8a9ab --- /dev/null +++ b/tests/ui/methods/opaque_param_in_ufc.stderr @@ -0,0 +1,36 @@ +error[E0599]: no function or associated item named `method` found for struct `Foo` in the current scope + --> $DIR/opaque_param_in_ufc.rs:17:15 + | +LL | struct Foo(T); + | ------------- function or associated item `method` not found for this struct +... +LL | Self::method(); + | ^^^^^^ function or associated item not found in `Foo` + | + = note: the function or associated item was found for + - `Foo` + +error[E0599]: no function or associated item named `method` found for struct `Foo` in the current scope + --> $DIR/opaque_param_in_ufc.rs:19:21 + | +LL | struct Foo(T); + | ------------- function or associated item `method` not found for this struct +... +LL | Foo::::method(); + | ^^^^^^ function or associated item not found in `Foo` + | + = note: the function or associated item was found for + - `Foo` + +error[E0599]: no function or associated item named `method2` found for struct `Foo` in the current scope + --> $DIR/opaque_param_in_ufc.rs:24:15 + | +LL | struct Foo(T); + | ------------- function or associated item `method2` not found for this struct +... +LL | Self::method2(x); + | ^^^^^^^ function or associated item not found in `Foo` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/methods/probe-overflow-due-to-sized-predicate-ordering.rs b/tests/ui/methods/probe-overflow-due-to-sized-predicate-ordering.rs new file mode 100644 index 0000000000000..08e15117c4b2a --- /dev/null +++ b/tests/ui/methods/probe-overflow-due-to-sized-predicate-ordering.rs @@ -0,0 +1,30 @@ +//@ check-pass +// Regression test due to #123279 + +pub trait Job: AsJob { + fn run_once(&self); +} + +impl Job for F { + fn run_once(&self) { + todo!() + } +} + +pub trait AsJob {} + +// Ensure that `T: Sized + Job` by reordering the explicit `Sized` to where +// the implicit sized pred would go. +impl AsJob for T {} + +pub struct LoopingJobService { + job: Box, +} + +impl Job for LoopingJobService { + fn run_once(&self) { + self.job.run_once() + } +} + +fn main() {} diff --git a/tests/ui/mir/alignment/packed.rs b/tests/ui/mir/alignment/packed.rs index fe8ecc668b868..1a12425f11a96 100644 --- a/tests/ui/mir/alignment/packed.rs +++ b/tests/ui/mir/alignment/packed.rs @@ -1,7 +1,7 @@ //@ run-pass //@ compile-flags: -C debug-assertions -#![feature(strict_provenance, pointer_is_aligned)] +#![feature(strict_provenance)] #[repr(packed)] struct Misaligner { diff --git a/tests/ui/mir/const_eval_select_cycle.rs b/tests/ui/mir/const_eval_select_cycle.rs new file mode 100644 index 0000000000000..0b84455b2f7fb --- /dev/null +++ b/tests/ui/mir/const_eval_select_cycle.rs @@ -0,0 +1,18 @@ +// Regression test for #122659 +//@ build-pass +//@ compile-flags: -O --crate-type=lib + +#![feature(core_intrinsics)] +#![feature(const_eval_select)] + +use std::intrinsics::const_eval_select; + +#[inline] +pub const fn f() { + const_eval_select((), g, g) +} + +#[inline] +pub const fn g() { + const_eval_select((), f, f) +} diff --git a/tests/ui/mismatched_types/binops.stderr b/tests/ui/mismatched_types/binops.stderr index 3585587ed4c02..f8047c8e2d449 100644 --- a/tests/ui/mismatched_types/binops.stderr +++ b/tests/ui/mismatched_types/binops.stderr @@ -73,15 +73,15 @@ LL | 5 < String::new(); | = help: the trait `PartialOrd` is not implemented for `{integer}` = help: the following other types implement trait `PartialOrd`: + f128 + f16 f32 f64 i128 i16 i32 i64 - i8 - isize - and 6 others + and 8 others error[E0277]: can't compare `{integer}` with `Result<{integer}, _>` --> $DIR/binops.rs:7:7 @@ -91,15 +91,15 @@ LL | 6 == Ok(1); | = help: the trait `PartialEq>` is not implemented for `{integer}` = help: the following other types implement trait `PartialEq`: + f128 + f16 f32 f64 i128 i16 i32 i64 - i8 - isize - and 6 others + and 8 others error: aborting due to 6 previous errors diff --git a/tests/ui/mismatched_types/generic-mismatch-reporting-issue-116615.rs b/tests/ui/mismatched_types/generic-mismatch-reporting-issue-116615.rs new file mode 100644 index 0000000000000..2bd10e762d9a0 --- /dev/null +++ b/tests/ui/mismatched_types/generic-mismatch-reporting-issue-116615.rs @@ -0,0 +1,14 @@ +fn foo(a: T, b: T) {} +fn foo_multi_same(a: T, b: T, c: T, d: T, e: T, f: i32) {} +fn foo_multi_generics(a: T, b: T, c: T, d: T, e: T, f: S, g: S) {} + +fn main() { + foo(1, 2.); + //~^ ERROR mismatched types + foo_multi_same("a", "b", false, true, (), 32); + //~^ ERROR arguments to this function are incorrect + foo_multi_generics("a", "b", "c", true, false, 32, 2.); + //~^ ERROR arguments to this function are incorrect + foo_multi_same("a", 1, 2, "d", "e", 32); + //~^ ERROR arguments to this function are incorrect +} diff --git a/tests/ui/mismatched_types/generic-mismatch-reporting-issue-116615.stderr b/tests/ui/mismatched_types/generic-mismatch-reporting-issue-116615.stderr new file mode 100644 index 0000000000000..a845dfabe93b7 --- /dev/null +++ b/tests/ui/mismatched_types/generic-mismatch-reporting-issue-116615.stderr @@ -0,0 +1,97 @@ +error[E0308]: mismatched types + --> $DIR/generic-mismatch-reporting-issue-116615.rs:6:12 + | +LL | foo(1, 2.); + | --- - ^^ expected integer, found floating-point number + | | | + | | expected all arguments to be this integer type because they need to match the type of this parameter + | arguments to this function are incorrect + | +note: function defined here + --> $DIR/generic-mismatch-reporting-issue-116615.rs:1:4 + | +LL | fn foo(a: T, b: T) {} + | ^^^ - ---- ---- this parameter needs to match the integer type of `a` + | | | + | | `b` needs to match the integer type of this parameter + | `a` and `b` all reference this parameter T + +error[E0308]: arguments to this function are incorrect + --> $DIR/generic-mismatch-reporting-issue-116615.rs:8:5 + | +LL | foo_multi_same("a", "b", false, true, (), 32); + | ^^^^^^^^^^^^^^ --- --- ----- ---- -- expected `&str`, found `()` + | | | | | + | | | | expected `&str`, found `bool` + | | | expected `&str`, found `bool` + | | expected some other arguments to be an `&str` type to match the type of this parameter + | expected some other arguments to be an `&str` type to match the type of this parameter + | +note: function defined here + --> $DIR/generic-mismatch-reporting-issue-116615.rs:2:4 + | +LL | fn foo_multi_same(a: T, b: T, c: T, d: T, e: T, f: i32) {} + | ^^^^^^^^^^^^^^ - ---- ---- ---- ---- ---- ------ + | | | | | | | + | | | | | | this parameter needs to match the `&str` type of `a` and `b` + | | | | | this parameter needs to match the `&str` type of `a` and `b` + | | | | this parameter needs to match the `&str` type of `a` and `b` + | | | `c`, `d` and `e` need to match the `&str` type of this parameter + | | `c`, `d` and `e` need to match the `&str` type of this parameter + | `a`, `b`, `c`, `d` and `e` all reference this parameter T + +error[E0308]: arguments to this function are incorrect + --> $DIR/generic-mismatch-reporting-issue-116615.rs:10:5 + | +LL | foo_multi_generics("a", "b", "c", true, false, 32, 2.); + | ^^^^^^^^^^^^^^^^^^ --- --- --- ---- ----- -- -- expected integer, found floating-point number + | | | | | | | + | | | | | | expected some other arguments to be an integer type to match the type of this parameter + | | | | | expected `&str`, found `bool` + | | | | expected `&str`, found `bool` + | | | expected some other arguments to be an `&str` type to match the type of this parameter + | | expected some other arguments to be an `&str` type to match the type of this parameter + | expected some other arguments to be an `&str` type to match the type of this parameter + | +note: function defined here + --> $DIR/generic-mismatch-reporting-issue-116615.rs:3:4 + | +LL | fn foo_multi_generics(a: T, b: T, c: T, d: T, e: T, f: S, g: S) {} + | ^^^^^^^^^^^^^^^^^^ - - ---- ---- ---- ---- ---- ---- ---- this parameter needs to match the integer type of `f` + | | | | | | | | | + | | | | | | | | `g` needs to match the integer type of this parameter + | | | | | | | this parameter needs to match the `&str` type of `a`, `b` and `c` + | | | | | | this parameter needs to match the `&str` type of `a`, `b` and `c` + | | | | | `d` and `e` need to match the `&str` type of this parameter + | | | | `d` and `e` need to match the `&str` type of this parameter + | | | `d` and `e` need to match the `&str` type of this parameter + | | `a`, `b`, `c`, `d` and `e` all reference this parameter T + | `f` and `g` all reference this parameter S + +error[E0308]: arguments to this function are incorrect + --> $DIR/generic-mismatch-reporting-issue-116615.rs:12:5 + | +LL | foo_multi_same("a", 1, 2, "d", "e", 32); + | ^^^^^^^^^^^^^^ --- - - --- --- expected some other arguments to be an `&str` type to match the type of this parameter + | | | | | + | | | | expected some other arguments to be an `&str` type to match the type of this parameter + | | | expected `&str`, found integer + | | expected `&str`, found integer + | expected some other arguments to be an `&str` type to match the type of this parameter + | +note: function defined here + --> $DIR/generic-mismatch-reporting-issue-116615.rs:2:4 + | +LL | fn foo_multi_same(a: T, b: T, c: T, d: T, e: T, f: i32) {} + | ^^^^^^^^^^^^^^ - ---- ---- ---- ---- ---- ------ + | | | | | | | + | | | | | | `b` and `c` need to match the `&str` type of this parameter + | | | | | `b` and `c` need to match the `&str` type of this parameter + | | | | this parameter needs to match the `&str` type of `a`, `d` and `e` + | | | this parameter needs to match the `&str` type of `a`, `d` and `e` + | | `b` and `c` need to match the `&str` type of this parameter + | `a`, `b`, `c`, `d` and `e` all reference this parameter T + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/issues/issue-1362.rs b/tests/ui/mismatched_types/issue-1362.rs similarity index 100% rename from tests/ui/issues/issue-1362.rs rename to tests/ui/mismatched_types/issue-1362.rs diff --git a/tests/ui/issues/issue-1362.stderr b/tests/ui/mismatched_types/issue-1362.stderr similarity index 100% rename from tests/ui/issues/issue-1362.stderr rename to tests/ui/mismatched_types/issue-1362.stderr diff --git a/tests/ui/issues/issue-1448-2.rs b/tests/ui/mismatched_types/issue-1448-2.rs similarity index 100% rename from tests/ui/issues/issue-1448-2.rs rename to tests/ui/mismatched_types/issue-1448-2.rs diff --git a/tests/ui/issues/issue-1448-2.stderr b/tests/ui/mismatched_types/issue-1448-2.stderr similarity index 100% rename from tests/ui/issues/issue-1448-2.stderr rename to tests/ui/mismatched_types/issue-1448-2.stderr diff --git a/tests/ui/mut/mut-ref.rs b/tests/ui/mut/mut-ref.rs index 80990b2bfdef0..54630d9500744 100644 --- a/tests/ui/mut/mut-ref.rs +++ b/tests/ui/mut/mut-ref.rs @@ -1,4 +1,11 @@ +//@ check-pass +#![allow(incomplete_features)] +#![feature(mut_ref)] fn main() { - let mut ref x = 10; //~ ERROR the order of `mut` and `ref` is incorrect - let ref mut y = 11; + let mut ref x = 10; + x = &11; + let ref mut y = 12; + *y = 13; + let mut ref mut z = 14; + z = &mut 15; } diff --git a/tests/ui/mut/mut-ref.stderr b/tests/ui/mut/mut-ref.stderr deleted file mode 100644 index b91392f223a8a..0000000000000 --- a/tests/ui/mut/mut-ref.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: the order of `mut` and `ref` is incorrect - --> $DIR/mut-ref.rs:2:9 - | -LL | let mut ref x = 10; - | ^^^^^^^ help: try switching the order: `ref mut` - -error: aborting due to 1 previous error - diff --git a/tests/ui/nll/match-cfg-fake-edges.rs b/tests/ui/nll/match-cfg-fake-edges.rs index 1afc7931a6b62..e349c2c8e2a5f 100644 --- a/tests/ui/nll/match-cfg-fake-edges.rs +++ b/tests/ui/nll/match-cfg-fake-edges.rs @@ -3,15 +3,57 @@ #![feature(if_let_guard)] +#[rustfmt::skip] +fn all_patterns_are_tested() { + // Even though `x` is never actually moved out of, we don't want borrowck results to be based on + // whether MIR lowering reveals which patterns are unreachable. + let x = String::new(); + match true { + _ => {}, + _ => drop(x), + } + // Borrowck must not know the second arm is never run. + drop(x); //~ ERROR use of moved value + + let x = String::new(); + if let _ = true { //~ WARN irrefutable + } else { + drop(x) + } + // Borrowck must not know the else branch is never run. + drop(x); //~ ERROR use of moved value + + let x = (String::new(), String::new()); + match x { + (y, _) | (_, y) => (), + } + &x.0; //~ ERROR borrow of moved value + // Borrowck must not know the second pattern never matches. + &x.1; //~ ERROR borrow of moved value + + let x = (String::new(), String::new()); + let ((y, _) | (_, y)) = x; + &x.0; //~ ERROR borrow of moved value + // Borrowck must not know the second pattern never matches. + &x.1; //~ ERROR borrow of moved value +} + +#[rustfmt::skip] fn guard_always_precedes_arm(y: i32) { - let mut x; // x should always be initialized, as the only way to reach the arm is // through the guard. + let mut x; match y { 0 | 2 if { x = 2; true } => x, _ => 2, }; + let mut x; + match y { + _ => 2, + 0 | 2 if { x = 2; true } => x, + }; + let mut x; match y { 0 | 2 if let Some(()) = { x = 2; Some(()) } => x, @@ -19,51 +61,58 @@ fn guard_always_precedes_arm(y: i32) { }; } +#[rustfmt::skip] fn guard_may_be_skipped(y: i32) { + // Even though x *is* always initialized, we don't want to have borrowck results be based on + // whether MIR lowering reveals which patterns are exhaustive. + let x; + match y { + _ if { x = 2; true } => {}, + // Borrowck must not know the guard is always run. + _ => drop(x), //~ ERROR used binding `x` is possibly-uninitialized + }; + let x; - // Even though x *is* always initialized, we don't want to have borrowck - // results be based on whether patterns are exhaustive. match y { _ if { x = 2; true } => 1, - _ if { - x; //~ ERROR E0381 - false - } => 2, + // Borrowck must not know the guard is always run. + _ if { x; false } => 2, //~ ERROR used binding `x` isn't initialized _ => 3, }; let x; match y { _ if let Some(()) = { x = 2; Some(()) } => 1, - _ if let Some(()) = { - x; //~ ERROR E0381 - None - } => 2, + _ if let Some(()) = { x; None } => 2, //~ ERROR used binding `x` isn't initialized _ => 3, }; } +#[rustfmt::skip] fn guard_may_be_taken(y: bool) { - let x = String::new(); // Even though x *is* never moved before the use, we don't want to have // borrowck results be based on whether patterns are disjoint. + let x = String::new(); + match y { + false if { drop(x); true } => {}, + // Borrowck must not know the guard is not run in the `true` case. + true => drop(x), //~ ERROR use of moved value: `x` + false => {}, + }; + + // Fine in the other order. + let x = String::new(); match y { - false if { drop(x); true } => 1, - true => { - x; //~ ERROR use of moved value: `x` - 2 - } - false => 3, + true => drop(x), + false if { drop(x); true } => {}, + false => {}, }; let x = String::new(); match y { - false if let Some(()) = { drop(x); Some(()) } => 1, - true => { - x; //~ ERROR use of moved value: `x` - 2 - } - false => 3, + false if let Some(()) = { drop(x); Some(()) } => {}, + true => drop(x), //~ ERROR use of moved value: `x` + false => {}, }; } diff --git a/tests/ui/nll/match-cfg-fake-edges.stderr b/tests/ui/nll/match-cfg-fake-edges.stderr index a6261345ceac7..d692ded36fa49 100644 --- a/tests/ui/nll/match-cfg-fake-edges.stderr +++ b/tests/ui/nll/match-cfg-fake-edges.stderr @@ -1,14 +1,128 @@ -error[E0381]: used binding `x` isn't initialized - --> $DIR/match-cfg-fake-edges.rs:29:13 +warning: irrefutable `if let` pattern + --> $DIR/match-cfg-fake-edges.rs:19:8 + | +LL | if let _ = true { + | ^^^^^^^^^^^^ + | + = 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 + +error[E0382]: use of moved value: `x` + --> $DIR/match-cfg-fake-edges.rs:16:10 + | +LL | let x = String::new(); + | - move occurs because `x` has type `String`, which does not implement the `Copy` trait +... +LL | _ => drop(x), + | - value moved here +... +LL | drop(x); + | ^ value used here after move + | +help: consider cloning the value if the performance cost is acceptable + | +LL | _ => drop(x.clone()), + | ++++++++ + +error[E0382]: use of moved value: `x` + --> $DIR/match-cfg-fake-edges.rs:24:10 + | +LL | let x = String::new(); + | - move occurs because `x` has type `String`, which does not implement the `Copy` trait +... +LL | drop(x) + | - value moved here +... +LL | drop(x); + | ^ value used here after move + | +help: consider cloning the value if the performance cost is acceptable + | +LL | drop(x.clone()) + | ++++++++ + +error[E0382]: borrow of moved value: `x.0` + --> $DIR/match-cfg-fake-edges.rs:30:5 + | +LL | (y, _) | (_, y) => (), + | - value moved here +LL | } +LL | &x.0; + | ^^^^ value borrowed here after move + | + = note: move occurs because `x.0` has type `String`, which does not implement the `Copy` trait +help: borrow this binding in the pattern to avoid moving the value + | +LL | (ref y, _) | (_, y) => (), + | +++ + +error[E0382]: borrow of moved value: `x.1` + --> $DIR/match-cfg-fake-edges.rs:32:5 + | +LL | (y, _) | (_, y) => (), + | - value moved here +... +LL | &x.1; + | ^^^^ value borrowed here after move + | + = note: move occurs because `x.1` has type `String`, which does not implement the `Copy` trait +help: borrow this binding in the pattern to avoid moving the value + | +LL | (y, _) | (_, ref y) => (), + | +++ + +error[E0382]: borrow of moved value: `x.0` + --> $DIR/match-cfg-fake-edges.rs:36:5 + | +LL | let ((y, _) | (_, y)) = x; + | - value moved here +LL | &x.0; + | ^^^^ value borrowed here after move + | + = note: move occurs because `x.0` has type `String`, which does not implement the `Copy` trait +help: borrow this binding in the pattern to avoid moving the value + | +LL | let ((ref y, _) | (_, y)) = x; + | +++ + +error[E0382]: borrow of moved value: `x.1` + --> $DIR/match-cfg-fake-edges.rs:38:5 + | +LL | let ((y, _) | (_, y)) = x; + | - value moved here +... +LL | &x.1; + | ^^^^ value borrowed here after move + | + = note: move occurs because `x.1` has type `String`, which does not implement the `Copy` trait +help: borrow this binding in the pattern to avoid moving the value + | +LL | let ((y, _) | (_, ref y)) = x; + | +++ + +error[E0381]: used binding `x` is possibly-uninitialized + --> $DIR/match-cfg-fake-edges.rs:72:19 | LL | let x; | - binding declared here but left uninitialized ... +LL | _ => drop(x), + | - ^ `x` used here but it is possibly-uninitialized + | | + | if this pattern is matched, `x` is not initialized + +error[E0381]: used binding `x` isn't initialized + --> $DIR/match-cfg-fake-edges.rs:79:16 + | +LL | let x; + | - binding declared here but left uninitialized +LL | match y { LL | _ if { x = 2; true } => 1, | ----- binding initialized here in some conditions -LL | _ if { -LL | x; - | ^ `x` used here but it isn't initialized +LL | // Borrowck must not know the guard is always run. +LL | _ if { x; false } => 2, + | ^ `x` used here but it isn't initialized | help: consider assigning a value | @@ -16,16 +130,15 @@ LL | let x = 0; | +++ error[E0381]: used binding `x` isn't initialized - --> $DIR/match-cfg-fake-edges.rs:39:13 + --> $DIR/match-cfg-fake-edges.rs:86:31 | LL | let x; | - binding declared here but left uninitialized LL | match y { LL | _ if let Some(()) = { x = 2; Some(()) } => 1, | ----- binding initialized here in some conditions -LL | _ if let Some(()) = { -LL | x; - | ^ `x` used here but it isn't initialized +LL | _ if let Some(()) = { x; None } => 2, + | ^ `x` used here but it isn't initialized | help: consider assigning a value | @@ -33,40 +146,39 @@ LL | let x = 0; | +++ error[E0382]: use of moved value: `x` - --> $DIR/match-cfg-fake-edges.rs:53:13 + --> $DIR/match-cfg-fake-edges.rs:99:22 | LL | let x = String::new(); | - move occurs because `x` has type `String`, which does not implement the `Copy` trait -... -LL | false if { drop(x); true } => 1, +LL | match y { +LL | false if { drop(x); true } => {}, | - value moved here -LL | true => { -LL | x; - | ^ value used here after move +LL | // Borrowck must not know the guard is not run in the `true` case. +LL | true => drop(x), + | ^ value used here after move | help: consider cloning the value if the performance cost is acceptable | -LL | false if { drop(x.clone()); true } => 1, +LL | false if { drop(x.clone()); true } => {}, | ++++++++ error[E0382]: use of moved value: `x` - --> $DIR/match-cfg-fake-edges.rs:63:13 + --> $DIR/match-cfg-fake-edges.rs:114:22 | LL | let x = String::new(); | - move occurs because `x` has type `String`, which does not implement the `Copy` trait LL | match y { -LL | false if let Some(()) = { drop(x); Some(()) } => 1, +LL | false if let Some(()) = { drop(x); Some(()) } => {}, | - value moved here -LL | true => { -LL | x; - | ^ value used here after move +LL | true => drop(x), + | ^ value used here after move | help: consider cloning the value if the performance cost is acceptable | -LL | false if let Some(()) = { drop(x.clone()); Some(()) } => 1, +LL | false if let Some(()) = { drop(x.clone()); Some(()) } => {}, | ++++++++ -error: aborting due to 4 previous errors +error: aborting due to 11 previous errors; 1 warning emitted Some errors have detailed explanations: E0381, E0382. For more information about an error, try `rustc --explain E0381`. diff --git a/tests/ui/nll/match-cfg-fake-edges2.rs b/tests/ui/nll/match-cfg-fake-edges2.rs index 48f95e03b78ca..ac90fb9cd1e76 100644 --- a/tests/ui/nll/match-cfg-fake-edges2.rs +++ b/tests/ui/nll/match-cfg-fake-edges2.rs @@ -5,13 +5,20 @@ fn all_previous_tests_may_be_done(y: &mut (bool, bool)) { let r = &mut y.1; // We don't actually test y.1 to select the second arm, but we don't want // borrowck results to be based on the order we match patterns. - match y { //~ ERROR cannot use `y.1` because it was mutably borrowed - (false, true) => 1, - (true, _) => { - r; - 2 - } - (false, _) => 3, + match y { + //~^ ERROR cannot use `y.1` because it was mutably borrowed + (false, true) => {} + // Borrowck must not know we don't test `y.1` when `y.0` is `true`. + (true, _) => drop(r), + (false, _) => {} + }; + + // Fine in the other order. + let r = &mut y.1; + match y { + (true, _) => drop(r), + (false, true) => {} + (false, _) => {} }; } diff --git a/tests/ui/nll/match-cfg-fake-edges2.stderr b/tests/ui/nll/match-cfg-fake-edges2.stderr index 639cba1406aef..0a228d62b9237 100644 --- a/tests/ui/nll/match-cfg-fake-edges2.stderr +++ b/tests/ui/nll/match-cfg-fake-edges2.stderr @@ -7,8 +7,8 @@ LL | let r = &mut y.1; LL | match y { | ^^^^^^^ use of borrowed `y.1` ... -LL | r; - | - borrow later used here +LL | (true, _) => drop(r), + | - borrow later used here error: aborting due to 1 previous error diff --git a/tests/ui/parser/fn-header-semantic-fail.rs b/tests/ui/parser/fn-header-semantic-fail.rs index 25d7c3f35fcaa..6ed173b6854db 100644 --- a/tests/ui/parser/fn-header-semantic-fail.rs +++ b/tests/ui/parser/fn-header-semantic-fail.rs @@ -48,6 +48,9 @@ fn main() { const fn fe3(); //~ ERROR functions in `extern` blocks cannot have qualifiers extern "C" fn fe4(); //~ ERROR functions in `extern` blocks cannot have qualifiers const async unsafe extern "C" fn fe5(); //~ ERROR functions in `extern` blocks - //~^ ERROR functions cannot be both `const` and `async` + //~| ERROR functions in `extern` blocks + //~| ERROR functions in `extern` blocks + //~| ERROR functions in `extern` blocks + //~| ERROR functions cannot be both `const` and `async` } } diff --git a/tests/ui/parser/fn-header-semantic-fail.stderr b/tests/ui/parser/fn-header-semantic-fail.stderr index 696d8e01b6387..cfc54839eb752 100644 --- a/tests/ui/parser/fn-header-semantic-fail.stderr +++ b/tests/ui/parser/fn-header-semantic-fail.stderr @@ -71,73 +71,75 @@ LL | const async unsafe extern "C" fn fi5() {} | `const` because of this error: functions in `extern` blocks cannot have qualifiers - --> $DIR/fn-header-semantic-fail.rs:46:18 + --> $DIR/fn-header-semantic-fail.rs:46:9 | LL | extern "C" { | ---------- in this `extern` block LL | async fn fe1(); - | ^^^ - | -help: remove the qualifiers - | -LL | fn fe1(); - | ~~ + | ^^^^^ help: remove this qualifier error: functions in `extern` blocks cannot have qualifiers - --> $DIR/fn-header-semantic-fail.rs:47:19 + --> $DIR/fn-header-semantic-fail.rs:47:9 | LL | extern "C" { | ---------- in this `extern` block LL | async fn fe1(); LL | unsafe fn fe2(); - | ^^^ - | -help: remove the qualifiers - | -LL | fn fe2(); - | ~~ + | ^^^^^^ help: remove this qualifier error: functions in `extern` blocks cannot have qualifiers - --> $DIR/fn-header-semantic-fail.rs:48:18 + --> $DIR/fn-header-semantic-fail.rs:48:9 | LL | extern "C" { | ---------- in this `extern` block ... LL | const fn fe3(); - | ^^^ - | -help: remove the qualifiers - | -LL | fn fe3(); - | ~~ + | ^^^^^ help: remove this qualifier error: functions in `extern` blocks cannot have qualifiers - --> $DIR/fn-header-semantic-fail.rs:49:23 + --> $DIR/fn-header-semantic-fail.rs:49:9 | LL | extern "C" { | ---------- in this `extern` block ... LL | extern "C" fn fe4(); - | ^^^ - | -help: remove the qualifiers + | ^^^^^^^^^^ help: remove this qualifier + +error: functions in `extern` blocks cannot have qualifiers + --> $DIR/fn-header-semantic-fail.rs:50:21 | -LL | fn fe4(); - | ~~ +LL | extern "C" { + | ---------- in this `extern` block +... +LL | const async unsafe extern "C" fn fe5(); + | ^^^^^^ help: remove this qualifier error: functions in `extern` blocks cannot have qualifiers - --> $DIR/fn-header-semantic-fail.rs:50:42 + --> $DIR/fn-header-semantic-fail.rs:50:15 | LL | extern "C" { | ---------- in this `extern` block ... LL | const async unsafe extern "C" fn fe5(); - | ^^^ + | ^^^^^ help: remove this qualifier + +error: functions in `extern` blocks cannot have qualifiers + --> $DIR/fn-header-semantic-fail.rs:50:9 | -help: remove the qualifiers +LL | extern "C" { + | ---------- in this `extern` block +... +LL | const async unsafe extern "C" fn fe5(); + | ^^^^^ help: remove this qualifier + +error: functions in `extern` blocks cannot have qualifiers + --> $DIR/fn-header-semantic-fail.rs:50:28 | -LL | fn fe5(); - | ~~ +LL | extern "C" { + | ---------- in this `extern` block +... +LL | const async unsafe extern "C" fn fe5(); + | ^^^^^^^^^^ help: remove this qualifier error: functions cannot be both `const` and `async` --> $DIR/fn-header-semantic-fail.rs:50:9 @@ -148,6 +150,6 @@ LL | const async unsafe extern "C" fn fe5(); | | `async` because of this | `const` because of this -error: aborting due to 14 previous errors +error: aborting due to 17 previous errors For more information about this error, try `rustc --explain E0379`. diff --git a/tests/ui/parser/no-const-fn-in-extern-block.rs b/tests/ui/parser/no-const-fn-in-extern-block.rs index 1993124edc394..d6c578681ccc7 100644 --- a/tests/ui/parser/no-const-fn-in-extern-block.rs +++ b/tests/ui/parser/no-const-fn-in-extern-block.rs @@ -3,6 +3,7 @@ extern "C" { //~^ ERROR functions in `extern` blocks cannot have qualifiers const unsafe fn bar(); //~^ ERROR functions in `extern` blocks cannot have qualifiers + //~| ERROR functions in `extern` blocks cannot have qualifiers } fn main() {} diff --git a/tests/ui/parser/no-const-fn-in-extern-block.stderr b/tests/ui/parser/no-const-fn-in-extern-block.stderr index 4ac0e265501f2..948ce669112c3 100644 --- a/tests/ui/parser/no-const-fn-in-extern-block.stderr +++ b/tests/ui/parser/no-const-fn-in-extern-block.stderr @@ -1,29 +1,28 @@ error: functions in `extern` blocks cannot have qualifiers - --> $DIR/no-const-fn-in-extern-block.rs:2:14 + --> $DIR/no-const-fn-in-extern-block.rs:2:5 | LL | extern "C" { | ---------- in this `extern` block LL | const fn foo(); - | ^^^ - | -help: remove the qualifiers - | -LL | fn foo(); - | ~~ + | ^^^^^ help: remove this qualifier error: functions in `extern` blocks cannot have qualifiers - --> $DIR/no-const-fn-in-extern-block.rs:4:21 + --> $DIR/no-const-fn-in-extern-block.rs:4:11 | LL | extern "C" { | ---------- in this `extern` block ... LL | const unsafe fn bar(); - | ^^^ - | -help: remove the qualifiers + | ^^^^^^ help: remove this qualifier + +error: functions in `extern` blocks cannot have qualifiers + --> $DIR/no-const-fn-in-extern-block.rs:4:5 | -LL | fn bar(); - | ~~ +LL | extern "C" { + | ---------- in this `extern` block +... +LL | const unsafe fn bar(); + | ^^^^^ help: remove this qualifier -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/parser/unsafe-foreign-mod-2.stderr b/tests/ui/parser/unsafe-foreign-mod-2.stderr index 7cc2de141ae14..fc05184f018f5 100644 --- a/tests/ui/parser/unsafe-foreign-mod-2.stderr +++ b/tests/ui/parser/unsafe-foreign-mod-2.stderr @@ -11,18 +11,13 @@ LL | extern "C" unsafe { | ^^^^^^ error: functions in `extern` blocks cannot have qualifiers - --> $DIR/unsafe-foreign-mod-2.rs:4:15 + --> $DIR/unsafe-foreign-mod-2.rs:4:5 | LL | extern "C" unsafe { | ----------------- in this `extern` block ... LL | unsafe fn foo(); - | ^^^ - | -help: remove the qualifiers - | -LL | fn foo(); - | ~~ + | ^^^^^^ help: remove this qualifier error: aborting due to 3 previous errors diff --git a/tests/ui/pattern/mut-ref-mut-2021.rs b/tests/ui/pattern/mut-ref-mut-2021.rs new file mode 100644 index 0000000000000..a3be40faeff46 --- /dev/null +++ b/tests/ui/pattern/mut-ref-mut-2021.rs @@ -0,0 +1,55 @@ +//@ edition: 2021 +#![allow(incomplete_features)] +#![feature(mut_ref)] + +struct Foo(u8); + +fn main() { + let Foo(a) = Foo(0); + a = 42; //~ ERROR [E0384] + + let Foo(mut a) = Foo(0); + a = 42; + + let Foo(ref a) = Foo(0); + a = &42; //~ ERROR [E0384] + + let Foo(mut ref a) = Foo(0); + a = &42; + + let Foo(ref mut a) = Foo(0); + a = &mut 42; //~ ERROR [E0384] + + let Foo(mut ref mut a) = Foo(0); + a = &mut 42; + + let Foo(a) = &Foo(0); + a = &42; //~ ERROR [E0384] + + let Foo(mut a) = &Foo(0); + a = 42; + + let Foo(ref a) = &Foo(0); + a = &42; //~ ERROR [E0384] + + let Foo(mut ref a) = &Foo(0); + a = &42; + + let Foo(a) = &mut Foo(0); + a = &mut 42; //~ ERROR [E0384] + + let Foo(mut a) = &mut Foo(0); + a = 42; + + let Foo(ref a) = &mut Foo(0); + a = &42; //~ ERROR [E0384] + + let Foo(mut ref a) = &mut Foo(0); + a = &42; + + let Foo(ref mut a) = &mut Foo(0); + a = &mut 42; //~ ERROR [E0384] + + let Foo(mut ref mut a) = &mut Foo(0); + a = &mut 42; +} diff --git a/tests/ui/pattern/mut-ref-mut-2021.stderr b/tests/ui/pattern/mut-ref-mut-2021.stderr new file mode 100644 index 0000000000000..eb31ffa0e30a2 --- /dev/null +++ b/tests/ui/pattern/mut-ref-mut-2021.stderr @@ -0,0 +1,70 @@ +error[E0384]: cannot assign twice to immutable variable `a` + --> $DIR/mut-ref-mut-2021.rs:9:5 + | +LL | let Foo(a) = Foo(0); + | - + | | + | first assignment to `a` + | help: consider making this binding mutable: `mut a` +LL | a = 42; + | ^^^^^^ cannot assign twice to immutable variable + +error[E0384]: cannot assign twice to immutable variable `a` + --> $DIR/mut-ref-mut-2021.rs:15:5 + | +LL | let Foo(ref a) = Foo(0); + | ----- first assignment to `a` +LL | a = &42; + | ^^^^^^^ cannot assign twice to immutable variable + +error[E0384]: cannot assign twice to immutable variable `a` + --> $DIR/mut-ref-mut-2021.rs:21:5 + | +LL | let Foo(ref mut a) = Foo(0); + | --------- first assignment to `a` +LL | a = &mut 42; + | ^^^^^^^^^^^ cannot assign twice to immutable variable + +error[E0384]: cannot assign twice to immutable variable `a` + --> $DIR/mut-ref-mut-2021.rs:27:5 + | +LL | let Foo(a) = &Foo(0); + | - first assignment to `a` +LL | a = &42; + | ^^^^^^^ cannot assign twice to immutable variable + +error[E0384]: cannot assign twice to immutable variable `a` + --> $DIR/mut-ref-mut-2021.rs:33:5 + | +LL | let Foo(ref a) = &Foo(0); + | ----- first assignment to `a` +LL | a = &42; + | ^^^^^^^ cannot assign twice to immutable variable + +error[E0384]: cannot assign twice to immutable variable `a` + --> $DIR/mut-ref-mut-2021.rs:39:5 + | +LL | let Foo(a) = &mut Foo(0); + | - first assignment to `a` +LL | a = &mut 42; + | ^^^^^^^^^^^ cannot assign twice to immutable variable + +error[E0384]: cannot assign twice to immutable variable `a` + --> $DIR/mut-ref-mut-2021.rs:45:5 + | +LL | let Foo(ref a) = &mut Foo(0); + | ----- first assignment to `a` +LL | a = &42; + | ^^^^^^^ cannot assign twice to immutable variable + +error[E0384]: cannot assign twice to immutable variable `a` + --> $DIR/mut-ref-mut-2021.rs:51:5 + | +LL | let Foo(ref mut a) = &mut Foo(0); + | --------- first assignment to `a` +LL | a = &mut 42; + | ^^^^^^^^^^^ cannot assign twice to immutable variable + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0384`. diff --git a/tests/ui/pattern/usefulness/unions.rs b/tests/ui/pattern/usefulness/unions.rs new file mode 100644 index 0000000000000..80a7f36a09a26 --- /dev/null +++ b/tests/ui/pattern/usefulness/unions.rs @@ -0,0 +1,35 @@ +fn main() { + #[derive(Copy, Clone)] + union U8AsBool { + n: u8, + b: bool, + } + + let x = U8AsBool { n: 1 }; + unsafe { + match x { + // exhaustive + U8AsBool { n: 2 } => {} + U8AsBool { b: true } => {} + U8AsBool { b: false } => {} + } + match x { + // exhaustive + U8AsBool { b: true } => {} + U8AsBool { n: 0 } => {} + U8AsBool { n: 1.. } => {} + } + match x { + //~^ ERROR non-exhaustive patterns: `U8AsBool { n: 0_u8 }` and `U8AsBool { b: false }` not covered + U8AsBool { b: true } => {} + U8AsBool { n: 1.. } => {} + } + // Our approach can report duplicate witnesses sometimes. + match (x, true) { + //~^ ERROR non-exhaustive patterns: `(U8AsBool { n: 0_u8 }, false)`, `(U8AsBool { b: false }, false)`, `(U8AsBool { n: 0_u8 }, false)` and 1 more not covered + (U8AsBool { b: true }, true) => {} + (U8AsBool { b: false }, true) => {} + (U8AsBool { n: 1.. }, true) => {} + } + } +} diff --git a/tests/ui/pattern/usefulness/unions.stderr b/tests/ui/pattern/usefulness/unions.stderr new file mode 100644 index 0000000000000..4b397dc25db8b --- /dev/null +++ b/tests/ui/pattern/usefulness/unions.stderr @@ -0,0 +1,34 @@ +error[E0004]: non-exhaustive patterns: `U8AsBool { n: 0_u8 }` and `U8AsBool { b: false }` not covered + --> $DIR/unions.rs:22:15 + | +LL | match x { + | ^ patterns `U8AsBool { n: 0_u8 }` and `U8AsBool { b: false }` not covered + | +note: `U8AsBool` defined here + --> $DIR/unions.rs:3:11 + | +LL | union U8AsBool { + | ^^^^^^^^ + = note: the matched value is of type `U8AsBool` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms + | +LL ~ U8AsBool { n: 1.. } => {}, +LL + U8AsBool { n: 0_u8 } | U8AsBool { b: false } => todo!() + | + +error[E0004]: non-exhaustive patterns: `(U8AsBool { n: 0_u8 }, false)`, `(U8AsBool { b: false }, false)`, `(U8AsBool { n: 0_u8 }, false)` and 1 more not covered + --> $DIR/unions.rs:28:15 + | +LL | match (x, true) { + | ^^^^^^^^^ patterns `(U8AsBool { n: 0_u8 }, false)`, `(U8AsBool { b: false }, false)`, `(U8AsBool { n: 0_u8 }, false)` and 1 more not covered + | + = note: the matched value is of type `(U8AsBool, bool)` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms + | +LL ~ (U8AsBool { n: 1.. }, true) => {}, +LL + _ => todo!() + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/privacy/generic_struct_field_projection.rs b/tests/ui/privacy/generic_struct_field_projection.rs new file mode 100644 index 0000000000000..c5bb1233c27b9 --- /dev/null +++ b/tests/ui/privacy/generic_struct_field_projection.rs @@ -0,0 +1,38 @@ +//! To determine all the types that need to be private when looking at `Struct`, we +//! used to invoke `predicates_of` to also look at types in `where` bounds. +//! Unfortunately this also computes the inferred outlives bounds, which means for +//! every field we check that if it is of type `&'a T` then `T: 'a` and if it is of +//! struct type, we check that the struct satisfies its lifetime parameters by looking +//! at its inferred outlives bounds. This means we end up with a `::Assoc: 'a` +//! in the outlives bounds of `Struct`. While this is trivially provable, privacy +//! only sees `Foo` and `Trait` and determines that `Foo` is private and then errors. +//! So now we invoke `explicit_predicates_of` to make sure we only care about user-written +//! predicates. + +//@ check-pass + +mod baz { + struct Foo; + + pub trait Trait { + type Assoc; + } + + impl Trait for Foo { + type Assoc = (); + } + + pub struct Bar<'a, T: Trait> { + source: &'a T::Assoc, + } + + pub struct Baz<'a> { + mode: Bar<'a, Foo>, + } +} + +pub struct Struct<'a> { + lexer: baz::Baz<'a>, +} + +fn main() {} diff --git a/tests/ui/proc-macro/auxiliary/api/mod.rs b/tests/ui/proc-macro/auxiliary/api/mod.rs index 199d097336af6..45ef6922d2834 100644 --- a/tests/ui/proc-macro/auxiliary/api/mod.rs +++ b/tests/ui/proc-macro/auxiliary/api/mod.rs @@ -5,8 +5,6 @@ #![crate_type = "proc-macro"] #![crate_name = "proc_macro_api_tests"] #![feature(proc_macro_span)] -#![feature(proc_macro_byte_character)] -#![feature(proc_macro_c_str_literals)] #![deny(dead_code)] // catch if a test function is never called extern crate proc_macro; diff --git a/tests/ui/proc-macro/bad-projection.rs b/tests/ui/proc-macro/bad-projection.rs index e633191bd310f..0769a7f08c127 100644 --- a/tests/ui/proc-macro/bad-projection.rs +++ b/tests/ui/proc-macro/bad-projection.rs @@ -15,4 +15,5 @@ pub fn uwu() -> <() as Project>::Assoc {} //~^ ERROR the trait bound `(): Project` is not satisfied //~| ERROR the trait bound `(): Project` is not satisfied //~| ERROR the trait bound `(): Project` is not satisfied +//~| ERROR the trait bound `(): Project` is not satisfied //~| ERROR function is expected to take 1 argument, but it takes 0 arguments diff --git a/tests/ui/proc-macro/bad-projection.stderr b/tests/ui/proc-macro/bad-projection.stderr index 8e0d8461849b4..2e8668f60de74 100644 --- a/tests/ui/proc-macro/bad-projection.stderr +++ b/tests/ui/proc-macro/bad-projection.stderr @@ -35,6 +35,18 @@ help: this trait has no implementations, consider adding one LL | trait Project { | ^^^^^^^^^^^^^ +error[E0277]: the trait bound `(): Project` is not satisfied + --> $DIR/bad-projection.rs:14:1 + | +LL | pub fn uwu() -> <() as Project>::Assoc {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 { + | ^^^^^^^^^^^^^ + error[E0277]: the trait bound `(): Project` is not satisfied --> $DIR/bad-projection.rs:14:40 | @@ -47,7 +59,7 @@ help: this trait has no implementations, consider adding one LL | trait Project { | ^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors Some errors have detailed explanations: E0277, E0593. For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/repr/repr-align.rs b/tests/ui/repr/repr-align.rs index 58ecf9a518327..33aa727d4bd0e 100644 --- a/tests/ui/repr/repr-align.rs +++ b/tests/ui/repr/repr-align.rs @@ -15,6 +15,10 @@ struct S2(i32); #[repr(align(536870912))] // ok: this is the largest accepted alignment struct S3(i32); +#[repr(align(0))] //~ ERROR: invalid `repr(align)` attribute: not a power of two + //~| ERROR: invalid `repr(align)` attribute: not a power of two +struct S4(i32); + #[repr(align(16.0))] //~ ERROR: invalid `repr(align)` attribute: not an unsuffixed integer //~| ERROR: invalid `repr(align)` attribute: not an unsuffixed integer enum E0 { A, B } @@ -30,4 +34,8 @@ enum E2 { A, B } #[repr(align(536870912))] // ok: this is the largest accepted alignment enum E3 { A, B } +#[repr(align(0))] //~ ERROR: invalid `repr(align)` attribute: not a power of two + //~| ERROR: invalid `repr(align)` attribute: not a power of two +enum E4 { A, B } + fn main() {} diff --git a/tests/ui/repr/repr-align.stderr b/tests/ui/repr/repr-align.stderr index bb0e17ba39558..660247840c48a 100644 --- a/tests/ui/repr/repr-align.stderr +++ b/tests/ui/repr/repr-align.stderr @@ -16,24 +16,36 @@ error[E0589]: invalid `repr(align)` attribute: larger than 2^29 LL | #[repr(align(4294967296))] | ^^^^^^^^^^ -error[E0589]: invalid `repr(align)` attribute: not an unsuffixed integer +error[E0589]: invalid `repr(align)` attribute: not a power of two --> $DIR/repr-align.rs:18:14 | +LL | #[repr(align(0))] + | ^ + +error[E0589]: invalid `repr(align)` attribute: not an unsuffixed integer + --> $DIR/repr-align.rs:22:14 + | LL | #[repr(align(16.0))] | ^^^^ error[E0589]: invalid `repr(align)` attribute: not a power of two - --> $DIR/repr-align.rs:22:14 + --> $DIR/repr-align.rs:26:14 | LL | #[repr(align(15))] | ^^ error[E0589]: invalid `repr(align)` attribute: larger than 2^29 - --> $DIR/repr-align.rs:26:14 + --> $DIR/repr-align.rs:30:14 | LL | #[repr(align(4294967296))] | ^^^^^^^^^^ +error[E0589]: invalid `repr(align)` attribute: not a power of two + --> $DIR/repr-align.rs:37:14 + | +LL | #[repr(align(0))] + | ^ + error[E0589]: invalid `repr(align)` attribute: not an unsuffixed integer --> $DIR/repr-align.rs:3:14 | @@ -58,16 +70,24 @@ LL | #[repr(align(4294967296))] | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0589]: invalid `repr(align)` attribute: not an unsuffixed integer +error[E0589]: invalid `repr(align)` attribute: not a power of two --> $DIR/repr-align.rs:18:14 | +LL | #[repr(align(0))] + | ^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0589]: invalid `repr(align)` attribute: not an unsuffixed integer + --> $DIR/repr-align.rs:22:14 + | LL | #[repr(align(16.0))] | ^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0589]: invalid `repr(align)` attribute: not a power of two - --> $DIR/repr-align.rs:22:14 + --> $DIR/repr-align.rs:26:14 | LL | #[repr(align(15))] | ^^ @@ -75,13 +95,21 @@ LL | #[repr(align(15))] = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0589]: invalid `repr(align)` attribute: larger than 2^29 - --> $DIR/repr-align.rs:26:14 + --> $DIR/repr-align.rs:30:14 | LL | #[repr(align(4294967296))] | ^^^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 12 previous errors +error[E0589]: invalid `repr(align)` attribute: not a power of two + --> $DIR/repr-align.rs:37:14 + | +LL | #[repr(align(0))] + | ^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 16 previous errors For more information about this error, try `rustc --explain E0589`. diff --git a/tests/ui/resolve/primitive-f16-f128-shadowed-mod.rs b/tests/ui/resolve/primitive-f16-f128-shadowed-mod.rs new file mode 100644 index 0000000000000..93f518201013a --- /dev/null +++ b/tests/ui/resolve/primitive-f16-f128-shadowed-mod.rs @@ -0,0 +1,19 @@ +//@ compile-flags: --crate-type=lib +//@ check-pass +//@ revisions: e2015 e2018 +// +//@[e2018] edition:2018 + +// Verify that gates for the `f16` and `f128` features do not apply to user modules +// See + +mod f16 { + pub fn a16() {} +} + +mod f128 { + pub fn a128() {} +} + +pub use f128::a128; +pub use f16::a16; diff --git a/tests/ui/resolve/primitive-f16-f128-shadowed.rs b/tests/ui/resolve/primitive-f16-f128-shadowed.rs index ed3fb44b256df..38c7e15bf5a20 100644 --- a/tests/ui/resolve/primitive-f16-f128-shadowed.rs +++ b/tests/ui/resolve/primitive-f16-f128-shadowed.rs @@ -1,5 +1,8 @@ //@ compile-flags: --crate-type=lib //@ check-pass +//@ revisions: e2015 e2018 +// +//@[e2018] edition:2018 // Verify that gates for the `f16` and `f128` features do not apply to user types diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.mir.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.mir.stderr deleted file mode 100644 index 7bbd4e158982f..0000000000000 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.mir.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/fn-ptr.rs:11:21 - | -LL | #[target_feature(enable = "sse2")] - | ---------------------------------- `#[target_feature]` added here -... -LL | let foo: fn() = foo; - | ---- ^^^ cannot coerce functions with `#[target_feature]` to safe function pointers - | | - | expected due to this - | - = note: expected fn pointer `fn()` - found fn item `fn() {foo}` - = note: fn items are distinct from fn pointers - = note: functions with `#[target_feature]` can only be coerced to `unsafe` function pointers -help: consider casting to a fn pointer - | -LL | let foo: fn() = foo as fn(); - | ~~~~~~~~~~~ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.thir.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.thir.stderr deleted file mode 100644 index 7bbd4e158982f..0000000000000 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.thir.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/fn-ptr.rs:11:21 - | -LL | #[target_feature(enable = "sse2")] - | ---------------------------------- `#[target_feature]` added here -... -LL | let foo: fn() = foo; - | ---- ^^^ cannot coerce functions with `#[target_feature]` to safe function pointers - | | - | expected due to this - | - = note: expected fn pointer `fn()` - found fn item `fn() {foo}` - = note: fn items are distinct from fn pointers - = note: functions with `#[target_feature]` can only be coerced to `unsafe` function pointers -help: consider casting to a fn pointer - | -LL | let foo: fn() = foo as fn(); - | ~~~~~~~~~~~ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.mir.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.mir.stderr deleted file mode 100644 index cabc475fa61cf..0000000000000 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.mir.stderr +++ /dev/null @@ -1,115 +0,0 @@ -error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:28:5 - | -LL | sse2(); - | ^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: sse2 - = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` - -error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:31:5 - | -LL | avx_bmi2(); - | ^^^^^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 - -error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:34:5 - | -LL | Quux.avx_bmi2(); - | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 - -error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:41:5 - | -LL | avx_bmi2(); - | ^^^^^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 - -error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:44:5 - | -LL | Quux.avx_bmi2(); - | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 - -error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:51:5 - | -LL | sse2(); - | ^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: sse2 - = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` - -error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:54:5 - | -LL | avx_bmi2(); - | ^^^^^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: bmi2 - -error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:57:5 - | -LL | Quux.avx_bmi2(); - | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: bmi2 - -error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:65:5 - | -LL | sse2(); - | ^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: sse2 - = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` - -error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:70:15 - | -LL | const _: () = sse2(); - | ^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: sse2 - = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` - -error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:74:15 - | -LL | const _: () = sse2_and_fxsr(); - | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target features: sse2 and fxsr - = note: the fxsr and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` - -error: call to function with `#[target_feature]` is unsafe and requires unsafe block (error E0133) - --> $DIR/safe-calls.rs:82:5 - | -LL | sse2(); - | ^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: sse2 - = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` -note: an unsafe function restricts its caller, but its body is safe by default - --> $DIR/safe-calls.rs:81:1 - | -LL | unsafe fn needs_unsafe_block() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: the lint level is defined here - --> $DIR/safe-calls.rs:78:8 - | -LL | #[deny(unsafe_op_in_unsafe_fn)] - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 12 previous errors - -For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.thir.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.thir.stderr deleted file mode 100644 index 13b58fde862d5..0000000000000 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.thir.stderr +++ /dev/null @@ -1,115 +0,0 @@ -error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:28:5 - | -LL | sse2(); - | ^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: sse2 - = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` - -error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:31:5 - | -LL | avx_bmi2(); - | ^^^^^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 - -error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:34:5 - | -LL | Quux.avx_bmi2(); - | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 - -error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:41:5 - | -LL | avx_bmi2(); - | ^^^^^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 - -error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:44:5 - | -LL | Quux.avx_bmi2(); - | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 - -error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:51:5 - | -LL | sse2(); - | ^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: sse2 - = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` - -error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:54:5 - | -LL | avx_bmi2(); - | ^^^^^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: bmi2 - -error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:57:5 - | -LL | Quux.avx_bmi2(); - | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: bmi2 - -error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:65:5 - | -LL | sse2(); - | ^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: sse2 - = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` - -error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:70:15 - | -LL | const _: () = sse2(); - | ^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: sse2 - = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` - -error[E0133]: call to function `sse2_and_fxsr` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:74:15 - | -LL | const _: () = sse2_and_fxsr(); - | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target features: sse2 and fxsr - = note: the fxsr and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` - -error: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe block (error E0133) - --> $DIR/safe-calls.rs:82:5 - | -LL | sse2(); - | ^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: sse2 - = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` -note: an unsafe function restricts its caller, but its body is safe by default - --> $DIR/safe-calls.rs:81:1 - | -LL | unsafe fn needs_unsafe_block() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: the lint level is defined here - --> $DIR/safe-calls.rs:78:8 - | -LL | #[deny(unsafe_op_in_unsafe_fn)] - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 12 previous errors - -For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/sanitizer/cfg.rs b/tests/ui/sanitizer/cfg.rs index 942141bd3fe3c..b1ba17d57139c 100644 --- a/tests/ui/sanitizer/cfg.rs +++ b/tests/ui/sanitizer/cfg.rs @@ -11,6 +11,7 @@ //@[cfi]compile-flags: -Clto -Ccodegen-units=1 //@[kcfi]needs-llvm-components: x86 //@[kcfi]compile-flags: -Zsanitizer=kcfi --cfg kcfi --target x86_64-unknown-none +//@[kcfi]compile-flags: -C panic=abort //@[leak]needs-sanitizer-leak //@[leak]compile-flags: -Zsanitizer=leak --cfg leak //@[memory]needs-sanitizer-memory diff --git a/tests/ui/sanitizer/cfi-async-closures.rs b/tests/ui/sanitizer/cfi-async-closures.rs new file mode 100644 index 0000000000000..d94f2992d8428 --- /dev/null +++ b/tests/ui/sanitizer/cfi-async-closures.rs @@ -0,0 +1,33 @@ +// Check various forms of dynamic closure calls + +//@ edition: 2021 +//@ revisions: cfi kcfi +// FIXME(#122848) Remove only-linux once OSX CFI binaries work +//@ only-linux +//@ [cfi] needs-sanitizer-cfi +//@ [kcfi] needs-sanitizer-kcfi +//@ compile-flags: -C target-feature=-crt-static +//@ [cfi] compile-flags: -C codegen-units=1 -C lto -C prefer-dynamic=off -C opt-level=0 +//@ [cfi] compile-flags: -Z sanitizer=cfi +//@ [kcfi] compile-flags: -Z sanitizer=kcfi +//@ [kcfi] compile-flags: -C panic=abort -Z panic-abort-tests -C prefer-dynamic=off +//@ run-pass + +#![feature(async_closure)] +#![feature(async_fn_traits)] + +use std::ops::AsyncFn; + +#[inline(never)] +fn identity(x: T) -> T { x } + +// We can't actually create a `dyn AsyncFn()`, because it's not object-safe, but we should check +// that we don't bug out when we encounter one. + +fn main() { + let f = identity(async || ()); + let _ = f.async_call(()); + let _ = f(); + let g: Box _> = Box::new(f) as _; + let _ = g(); +} diff --git a/tests/ui/sanitizer/cfi-closure-fn-ptr-cast.rs b/tests/ui/sanitizer/cfi-closure-fn-ptr-cast.rs deleted file mode 100644 index 1ae494d87d425..0000000000000 --- a/tests/ui/sanitizer/cfi-closure-fn-ptr-cast.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Tests that converting a closure to a function pointer works -// The notable thing being tested here is that when the closure does not capture anything, -// the call method from its Fn trait takes a ZST representing its environment. The compiler then -// uses the assumption that the ZST is non-passed to reify this into a function pointer. -// -// This checks that the reified function pointer will have the expected alias set at its call-site. - -//@ revisions: cfi kcfi -// FIXME(#122848) Remove only-linux once OSX CFI binaries work -//@ only-linux -//@ [cfi] needs-sanitizer-cfi -//@ [kcfi] needs-sanitizer-kcfi -//@ compile-flags: -C target-feature=-crt-static -//@ [cfi] compile-flags: -C codegen-units=1 -C lto -C prefer-dynamic=off -C opt-level=0 -//@ [cfi] compile-flags: -Z sanitizer=cfi -//@ [kcfi] compile-flags: -Z sanitizer=kcfi -//@ run-pass - -pub fn main() { - let f: &fn() = &((|| ()) as _); - f(); -} diff --git a/tests/ui/sanitizer/cfi-closures.rs b/tests/ui/sanitizer/cfi-closures.rs new file mode 100644 index 0000000000000..9f9002da674f5 --- /dev/null +++ b/tests/ui/sanitizer/cfi-closures.rs @@ -0,0 +1,90 @@ +// Check various forms of dynamic closure calls + +//@ revisions: cfi kcfi +// FIXME(#122848) Remove only-linux once OSX CFI binaries work +//@ only-linux +//@ [cfi] needs-sanitizer-cfi +//@ [kcfi] needs-sanitizer-kcfi +//@ compile-flags: -C target-feature=-crt-static +//@ [cfi] compile-flags: -C codegen-units=1 -C lto -C prefer-dynamic=off -C opt-level=0 +//@ [cfi] compile-flags: -Z sanitizer=cfi +//@ [kcfi] compile-flags: -Z sanitizer=kcfi +//@ [kcfi] compile-flags: -C panic=abort -Z panic-abort-tests -C prefer-dynamic=off +//@ compile-flags: --test +//@ run-pass + +#![feature(fn_traits)] +#![feature(unboxed_closures)] + +fn foo<'a, T>() -> Box &'a T> { + Box::new(|x| x) +} + +#[test] +fn dyn_fn_with_params() { + let x = 3; + let f = foo(); + f(&x); + // FIXME remove once drops are working. + std::mem::forget(f); +} + +#[test] +fn call_fn_trait() { + let f: &(dyn Fn()) = &(|| {}) as _; + f.call(()); +} + +#[test] +fn fn_ptr_cast() { + let f: &fn() = &((|| ()) as _); + f(); +} + +fn use_fnmut(mut f: F) { + f() +} + +#[test] +fn fn_to_fnmut() { + let f: &(dyn Fn()) = &(|| {}) as _; + use_fnmut(f); +} + +fn hrtb_helper(f: &dyn for<'a> Fn(&'a usize)) { + f(&10) +} + +#[test] +fn hrtb_fn() { + hrtb_helper((&|x: &usize| println!("{}", *x)) as _) +} + +#[test] +fn fnonce() { + let f: Box = Box::new(|| {}) as _; + f(); +} + +fn use_closure(call: extern "rust-call" fn(&C, ()) -> i32, f: &C) -> i32 { + call(f, ()) +} + +#[test] +fn closure_addr_taken() { + let x = 3i32; + let f = || x; + let call = Fn::<()>::call; + use_closure(call, &f); +} + +fn use_closure_once(call: extern "rust-call" fn(C, ()) -> i32, f: C) -> i32 { + call(f, ()) +} + +#[test] +fn closure_once_addr_taken() { + let g = || 3; + let call2 = FnOnce::<()>::call_once; + use_closure_once(call2, g); +} diff --git a/tests/ui/sanitizer/cfi-complex-receiver.rs b/tests/ui/sanitizer/cfi-complex-receiver.rs index 52095a384b25d..c7b45a775ca1d 100644 --- a/tests/ui/sanitizer/cfi-complex-receiver.rs +++ b/tests/ui/sanitizer/cfi-complex-receiver.rs @@ -11,6 +11,7 @@ //@ [cfi] compile-flags: -C codegen-units=1 -C lto -C prefer-dynamic=off -C opt-level=0 //@ [cfi] compile-flags: -Z sanitizer=cfi //@ [kcfi] compile-flags: -Z sanitizer=kcfi +//@ [kcfi] compile-flags: -C panic=abort -C prefer-dynamic=off //@ run-pass use std::sync::Arc; diff --git a/tests/ui/sanitizer/cfi-coroutine.rs b/tests/ui/sanitizer/cfi-coroutine.rs new file mode 100644 index 0000000000000..5c6a489a7e89e --- /dev/null +++ b/tests/ui/sanitizer/cfi-coroutine.rs @@ -0,0 +1,67 @@ +// Verifies that we can call dynamic coroutines + +//@ revisions: cfi kcfi +// FIXME(#122848) Remove only-linux once OSX CFI binaries work +//@ only-linux +//@ edition: 2024 +//@ [cfi] needs-sanitizer-cfi +//@ [kcfi] needs-sanitizer-kcfi +//@ compile-flags: -C target-feature=-crt-static +//@ [cfi] compile-flags: -C codegen-units=1 -C lto -C prefer-dynamic=off -C opt-level=0 +//@ [cfi] compile-flags: -Z sanitizer=cfi +//@ [kcfi] compile-flags: -Z sanitizer=kcfi +//@ [kcfi] compile-flags: -C panic=abort -Z panic-abort-tests -C prefer-dynamic=off +//@ compile-flags: --test -Z unstable-options +//@ run-pass + +#![feature(coroutines)] +#![feature(coroutine_trait)] +#![feature(noop_waker)] +#![feature(gen_blocks)] +#![feature(async_iterator)] + +use std::ops::{Coroutine, CoroutineState}; +use std::pin::{pin, Pin}; +use std::task::{Context, Poll, Waker}; +use std::async_iter::AsyncIterator; + +#[test] +fn general_coroutine() { + let mut coro = |x: i32| { + yield x; + "done" + }; + let mut abstract_coro: Pin<&mut dyn Coroutine> = pin!(coro); + assert_eq!(abstract_coro.as_mut().resume(2), CoroutineState::Yielded(2)); + assert_eq!(abstract_coro.as_mut().resume(0), CoroutineState::Complete("done")); +} + +async fn async_fn() {} + +#[test] +fn async_coroutine() { + let f: fn() -> Pin>> = || Box::pin(async_fn()); + let _ = async { f().await; }; + assert_eq!(f().as_mut().poll(&mut Context::from_waker(Waker::noop())), Poll::Ready(())); +} + +async gen fn async_gen_fn() -> u8 { + yield 5; +} + +#[test] +fn async_gen_coroutine() { + let f: fn() -> Pin>> = || Box::pin(async_gen_fn()); + assert_eq!(f().as_mut().poll_next(&mut Context::from_waker(Waker::noop())), + Poll::Ready(Some(5))); +} + +gen fn gen_fn() -> u8 { + yield 6; +} + +#[test] +fn gen_coroutine() { + let f: fn() -> Box> = || Box::new(gen_fn()); + assert_eq!(f().next(), Some(6)); +} diff --git a/tests/ui/sanitizer/cfi-method-fn-ptr-cast.rs b/tests/ui/sanitizer/cfi-method-fn-ptr-cast.rs new file mode 100644 index 0000000000000..8f79de1174883 --- /dev/null +++ b/tests/ui/sanitizer/cfi-method-fn-ptr-cast.rs @@ -0,0 +1,57 @@ +// Verifies that casting a method to a function pointer works. + +//@ revisions: cfi kcfi +// FIXME(#122848) Remove only-linux once OSX CFI binaries work +//@ only-linux +//@ [cfi] needs-sanitizer-cfi +//@ [kcfi] needs-sanitizer-kcfi +//@ compile-flags: -C target-feature=-crt-static +//@ [cfi] compile-flags: -C opt-level=0 -C codegen-units=1 -C lto +//@ [cfi] compile-flags: -C prefer-dynamic=off +//@ [cfi] compile-flags: -Z sanitizer=cfi +//@ [kcfi] compile-flags: -Z sanitizer=kcfi +//@ [kcfi] compile-flags: -C panic=abort -C prefer-dynamic=off +//@ run-pass + +trait Foo { + fn foo(&self); + fn bar(&self); +} + +struct S; + +impl Foo for S { + fn foo(&self) {} + #[track_caller] + fn bar(&self) {} +} + +struct S2 { + f: fn(&S) +} + +impl S2 { + fn foo(&self, s: &S) { + (self.f)(s) + } +} + +trait Trait1 { + fn foo(&self); +} + +struct Type1; + +impl Trait1 for Type1 { + fn foo(&self) {} +} + +fn main() { + let type1 = Type1 {}; + let f = ::foo; + f(&type1); + // Check again with different optimization barriers + S2 { f: ::foo }.foo(&S); + // Check mismatched #[track_caller] + S2 { f: ::bar }.foo(&S) +} diff --git a/tests/ui/sanitizer/cfi-self-ref.rs b/tests/ui/sanitizer/cfi-self-ref.rs index f8793aec6e218..3b524ac661cf8 100644 --- a/tests/ui/sanitizer/cfi-self-ref.rs +++ b/tests/ui/sanitizer/cfi-self-ref.rs @@ -9,6 +9,7 @@ //@ [cfi] compile-flags: -C codegen-units=1 -C lto -C prefer-dynamic=off -C opt-level=0 //@ [cfi] compile-flags: -Z sanitizer=cfi //@ [kcfi] compile-flags: -Z sanitizer=kcfi +//@ [kcfi] compile-flags: -C panic=abort -C prefer-dynamic=off //@ run-pass use std::marker::PhantomData; diff --git a/tests/ui/sanitizer/cfi-supertraits.rs b/tests/ui/sanitizer/cfi-supertraits.rs new file mode 100644 index 0000000000000..ed3d722ebb78e --- /dev/null +++ b/tests/ui/sanitizer/cfi-supertraits.rs @@ -0,0 +1,73 @@ +#![feature(trait_upcasting)] +// Check that super-traits are callable. + +//@ revisions: cfi kcfi +// FIXME(#122848) Remove only-linux once OSX CFI binaries work +//@ only-linux +//@ [cfi] needs-sanitizer-cfi +//@ [kcfi] needs-sanitizer-kcfi +//@ compile-flags: -C target-feature=-crt-static +//@ [cfi] compile-flags: -C codegen-units=1 -C lto -C prefer-dynamic=off -C opt-level=0 +//@ [cfi] compile-flags: -Z sanitizer=cfi +//@ [kcfi] compile-flags: -Z sanitizer=kcfi +//@ [kcfi] compile-flags: -C panic=abort -C prefer-dynamic=off +//@ run-pass + +trait Parent1 { + type P1; + fn p1(&self) -> Self::P1; +} + +trait Parent2 { + type P2; + fn p2(&self) -> Self::P2; +} + +trait Child : Parent1 + Parent2 { + type C; + fn c(&self) -> Self::C; +} + +struct Foo; + +impl Parent1 for Foo { + type P1 = u16; + fn p1(&self) -> Self::P1 { + println!("p1"); + 1 + } +} + +impl Parent2 for Foo { + type P2 = u32; + fn p2(&self) -> Self::P2 { + println!("p2"); + 2 + } +} + +impl Child for Foo { + type C = u8; + fn c(&self) -> Self::C { + println!("c"); + 0 + } +} + +fn main() { + // Child can access its own methods and super methods. + let x = &Foo as &dyn Child; + x.c(); + x.p1(); + x.p2(); + // Parents can be created and access their methods. + let y = &Foo as &dyn Parent1; + y.p1(); + let z = &Foo as &dyn Parent2; + z.p2(); + // Trait upcasting works + let x1 = x as &dyn Parent1; + x1.p1(); + let x2 = x as &dyn Parent2; + x2.p2(); +} diff --git a/tests/ui/sanitizer/cfi-virtual-auto.rs b/tests/ui/sanitizer/cfi-virtual-auto.rs index 517c3d49f765a..6971d516a2057 100644 --- a/tests/ui/sanitizer/cfi-virtual-auto.rs +++ b/tests/ui/sanitizer/cfi-virtual-auto.rs @@ -9,6 +9,7 @@ //@ [cfi] compile-flags: -C codegen-units=1 -C lto -C prefer-dynamic=off -C opt-level=0 //@ [cfi] compile-flags: -Z sanitizer=cfi //@ [kcfi] compile-flags: -Z sanitizer=kcfi +//@ [kcfi] compile-flags: -C panic=abort -C prefer-dynamic=off //@ run-pass trait Foo { diff --git a/tests/ui/self/arbitrary-self-opaque.rs b/tests/ui/self/arbitrary-self-opaque.rs new file mode 100644 index 0000000000000..99357dde3e149 --- /dev/null +++ b/tests/ui/self/arbitrary-self-opaque.rs @@ -0,0 +1,12 @@ +#![feature(type_alias_impl_trait)] +struct Foo; + +type Bar = impl Sized; +//~^ ERROR unconstrained opaque type + +impl Foo { + fn foo(self: Bar) {} + //~^ ERROR: invalid `self` parameter type: Bar +} + +fn main() {} diff --git a/tests/ui/self/arbitrary-self-opaque.stderr b/tests/ui/self/arbitrary-self-opaque.stderr new file mode 100644 index 0000000000000..6b5db8d849329 --- /dev/null +++ b/tests/ui/self/arbitrary-self-opaque.stderr @@ -0,0 +1,20 @@ +error: unconstrained opaque type + --> $DIR/arbitrary-self-opaque.rs:4:12 + | +LL | type Bar = impl Sized; + | ^^^^^^^^^^ + | + = note: `Bar` must be used in combination with a concrete type within the same module + +error[E0307]: invalid `self` parameter type: Bar + --> $DIR/arbitrary-self-opaque.rs:8:18 + | +LL | fn foo(self: Bar) {} + | ^^^ + | + = note: type of `self` must be `Self` or a type that dereferences to it + = help: consider changing to `self`, `&self`, `&mut self`, `self: Box`, `self: Rc`, `self: Arc`, or `self: Pin