diff options
572 files changed, 9797 insertions, 5752 deletions
diff --git a/.mailmap b/.mailmap index 9366ef383fc..ac221fa3a60 100644 --- a/.mailmap +++ b/.mailmap @@ -129,6 +129,7 @@ Jakub Adam Wieczorek <jakub.adam.wieczorek@gmail.com> <jakub.bukaj@yahoo.com> Jakub Adam Wieczorek <jakub.adam.wieczorek@gmail.com> <jakub@jakub.cc> Jakub Adam Wieczorek <jakub.adam.wieczorek@gmail.com> <jakubw@jakubw.net> James Deng <cnjamesdeng@gmail.com> <cnJamesDeng@gmail.com> +James Hinshelwood <jameshinshelwood1@gmail.com> <james.hinshelwood@bigpayme.com> James Miller <bladeon@gmail.com> <james@aatch.net> James Perry <james.austin.perry@gmail.com> Jason Fager <jfager@gmail.com> diff --git a/Cargo.lock b/Cargo.lock index 50a5d78731f..5fda666e480 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,6 +25,17 @@ dependencies = [ ] [[package]] +name = "ahash" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43bb833f0bf979d8475d38fbf09ed3b8a55e1885fe93ad3f93239fc6a4f17b98" +dependencies = [ + "getrandom 0.2.0", + "once_cell", + "version_check", +] + +[[package]] name = "aho-corasick" version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -104,6 +115,47 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a2f58b0bb10c380af2b26e57212856b8c9a59e0925b4c20f4a174a49734eaf7" [[package]] +name = "askama" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d8f355701c672c2ba3d718acbd213f740beea577cc4eae66accdffe15be1882" +dependencies = [ + "askama_derive", + "askama_escape", + "askama_shared", +] + +[[package]] +name = "askama_derive" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84704cab5b7ae0fd3a9f78ee5eb7b27f3749df445f04623db6633459ae283267" +dependencies = [ + "askama_shared", + "proc-macro2", + "syn", +] + +[[package]] +name = "askama_escape" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a1bb320f97e6edf9f756bf015900038e43c7700e059688e5724a928c8f3b8d5" + +[[package]] +name = "askama_shared" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae03eebba55a2697a376e58b573a29fe36893157173ac8df312ad85f3c0e012" +dependencies = [ + "askama_escape", + "nom", + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -283,7 +335,7 @@ dependencies = [ "cargo-test-macro", "cargo-test-support", "cargo-util", - "clap", + "clap 3.0.6", "crates-io", "crossbeam-utils 0.8.3", "curl", @@ -563,16 +615,31 @@ dependencies = [ "ansi_term 0.12.1", "atty", "bitflags", - "strsim", - "textwrap", + "strsim 0.8.0", + "textwrap 0.11.0", "unicode-width", "vec_map", "yaml-rust 0.3.5", ] [[package]] +name = "clap" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1957aa4a5fb388f0a0a73ce7556c5b42025b874e5cdc2c670775e346e97adec0" +dependencies = [ + "atty", + "bitflags", + "indexmap", + "os_str_bytes", + "strsim 0.10.0", + "termcolor", + "textwrap 0.14.2", +] + +[[package]] name = "clippy" -version = "0.1.59" +version = "0.1.60" dependencies = [ "cargo_metadata 0.14.0", "clippy_lints", @@ -580,6 +647,7 @@ dependencies = [ "compiletest_rs", "derive-new", "filetime", + "futures 0.3.12", "if_chain", "itertools 0.10.1", "parking_lot", @@ -592,6 +660,7 @@ dependencies = [ "syn", "tempfile", "tester", + "tokio", ] [[package]] @@ -600,7 +669,7 @@ version = "0.0.1" dependencies = [ "bytecount", "cargo_metadata 0.14.0", - "clap", + "clap 2.34.0", "indoc", "itertools 0.10.1", "opener", @@ -611,7 +680,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.59" +version = "0.1.60" dependencies = [ "cargo_metadata 0.14.0", "clippy_utils", @@ -632,8 +701,9 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.59" +version = "0.1.60" dependencies = [ + "arrayvec", "if_chain", "rustc-semver", ] @@ -1510,17 +1580,6 @@ dependencies = [ ] [[package]] -name = "globwalk" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" -dependencies = [ - "bitflags", - "ignore", - "walkdir", -] - -[[package]] name = "gsgdt" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1549,6 +1608,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "362385356d610bd1e5a408ddf8d022041774b683f345a1d2cfcb4f60f8ae2db5" dependencies = [ + "ahash", "compiler_builtins", "rustc-std-workspace-alloc", "rustc-std-workspace-core", @@ -1716,7 +1776,7 @@ name = "installer" version = "0.0.0" dependencies = [ "anyhow", - "clap", + "clap 2.34.0", "flate2", "lazy_static", "num_cpus", @@ -2161,7 +2221,7 @@ dependencies = [ "ammonia", "anyhow", "chrono", - "clap", + "clap 2.34.0", "elasticlunr-rs", "env_logger 0.7.1", "handlebars", @@ -2246,6 +2306,12 @@ dependencies = [ ] [[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] name = "miniz_oxide" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2305,6 +2371,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" [[package]] +name = "nom" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109" +dependencies = [ + "memchr", + "minimal-lexical", + "version_check", +] + +[[package]] name = "ntapi" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2349,8 +2426,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39f37e50073ccad23b6d09bcb5b263f4e76d3bb6038e4a3c08e52162ffa8abc2" dependencies = [ "compiler_builtins", - "crc32fast", - "indexmap", "memchr", "rustc-std-workspace-alloc", "rustc-std-workspace-core", @@ -2369,6 +2444,18 @@ dependencies = [ ] [[package]] +name = "object" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ce8b38d41f9f3618fc23f908faae61510f8d8ce2d99cbe910641e8f1971f084" +dependencies = [ + "crc32fast", + "hashbrown", + "indexmap", + "memchr", +] + +[[package]] name = "odht" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2466,6 +2553,15 @@ dependencies = [ ] [[package]] +name = "os_str_bytes" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" +dependencies = [ + "memchr", +] + +[[package]] name = "output_vt100" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2869,7 +2965,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fec2e85e7a30f8fd31b7cf288ad363b5e51fd2cb6f53b416b0cfaabd84e1ccb" dependencies = [ "bitflags", - "clap", + "clap 2.34.0", "derive_more", "env_logger 0.7.1", "humantime 2.0.1", @@ -3224,7 +3320,7 @@ dependencies = [ name = "rustbook" version = "0.1.0" dependencies = [ - "clap", + "clap 2.34.0", "env_logger 0.7.1", "mdbook", ] @@ -3754,7 +3850,7 @@ dependencies = [ "itertools 0.9.0", "jobserver", "libc", - "object 0.26.2", + "object 0.28.1", "pathdiff", "regex", "rustc_apfloat", @@ -4631,6 +4727,7 @@ name = "rustdoc" version = "0.0.0" dependencies = [ "arrayvec", + "askama", "expect-test", "itertools 0.9.0", "minifier", @@ -4642,7 +4739,6 @@ dependencies = [ "serde_json", "smallvec", "tempfile", - "tera", "tracing", "tracing-subscriber", "tracing-tree", @@ -5087,12 +5183,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] name = "structopt" version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40b9788f4202aa75c240ecc9c15c65185e6a39ccdeb0fd5d008b98825464c87c" dependencies = [ - "clap", + "clap 2.34.0", "lazy_static", "structopt-derive", ] @@ -5188,21 +5290,6 @@ dependencies = [ ] [[package]] -name = "tera" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81060acb882480c8793782eb96bc86f5c83d2fc7175ad46c375c6956ef7afa62" -dependencies = [ - "globwalk", - "lazy_static", - "pest", - "pest_derive", - "regex", - "serde", - "serde_json", -] - -[[package]] name = "term" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5279,6 +5366,12 @@ dependencies = [ ] [[package]] +name = "textwrap" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80" + +[[package]] name = "thiserror" version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/RELEASES.md b/RELEASES.md index 4b9b20f4cba..460c78b14d1 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,137 @@ +Version 1.58.0 (2022-01-13) +========================== + +Language +-------- + +- [Format strings can now capture arguments simply by writing `{ident}` in the string.][90473] This works in all macros accepting format strings. Support for this in `panic!` (`panic!("{ident}")`) requires the 2021 edition; panic invocations in previous editions that appear to be trying to use this will result in a warning lint about not having the intended effect. +- [`*const T` pointers can now be dereferenced in const contexts.][89551] +- [The rules for when a generic struct implements `Unsize` have been relaxed.][90417] + +Compiler +-------- + +- [Add LLVM CFI support to the Rust compiler][89652] +- [Stabilize -Z strip as -C strip][90058]. Note that while release builds already don't add debug symbols for the code you compile, the compiled standard library that ships with Rust includes debug symbols, so you may want to use the `strip` option to remove these symbols to produce smaller release binaries. Note that this release only includes support in rustc, not directly in cargo. +- [Add support for LLVM coverage mapping format versions 5 and 6][91207] +- [Emit LLVM optimization remarks when enabled with `-Cremark`][90833] +- [Update the minimum external LLVM to 12][90175] +- [Add `x86_64-unknown-none` at Tier 3*][89062] +- [Build musl dist artifacts with debuginfo enabled][90733]. When building release binaries using musl, you may want to use the newly stabilized strip option to remove these debug symbols, reducing the size of your binaries. +- [Don't abort compilation after giving a lint error][87337] +- [Error messages point at the source of trait bound obligations in more places][89580] + +\* Refer to Rust's [platform support page][platform-support-doc] for more + information on Rust's tiered platform support. + +Libraries +--------- + +- [All remaining functions in the standard library have `#[must_use]` annotations where appropriate][89692], producing a warning when ignoring their return value. This helps catch mistakes such as expecting a function to mutate a value in place rather than return a new value. +- [Paths are automatically canonicalized on Windows for operations that support it][89174] +- [Re-enable debug checks for `copy` and `copy_nonoverlapping`][90041] +- [Implement `RefUnwindSafe` for `Rc<T>`][87467] +- [Make RSplit<T, P>: Clone not require T: Clone][90117] +- [Implement `Termination` for `Result<Infallible, E>`][88601]. This allows writing `fn main() -> Result<Infallible, ErrorType>`, for a program whose successful exits never involve returning from `main` (for instance, a program that calls `exit`, or that uses `exec` to run another program). + +Stabilized APIs +--------------- + +- [`Metadata::is_symlink`] +- [`Path::is_symlink`] +- [`{integer}::saturating_div`] +- [`Option::unwrap_unchecked`] +- [`Result::unwrap_unchecked`] +- [`Result::unwrap_err_unchecked`] +- [`File::options`] + +These APIs are now usable in const contexts: + +- [`Duration::new`] +- [`Duration::checked_add`] +- [`Duration::saturating_add`] +- [`Duration::checked_sub`] +- [`Duration::saturating_sub`] +- [`Duration::checked_mul`] +- [`Duration::saturating_mul`] +- [`Duration::checked_div`] + +Cargo +----- + +- [Add --message-format for install command][cargo/10107] +- [Warn when alias shadows external subcommand][cargo/10082] + +Rustdoc +------- + +- [Show all Deref implementations recursively in rustdoc][90183] +- [Use computed visibility in rustdoc][88447] + +Compatibility Notes +------------------- + +- [Try all stable method candidates first before trying unstable ones][90329]. This change ensures that adding new nightly-only methods to the Rust standard library will not break code invoking methods of the same name from traits outside the standard library. +- Windows: [`std::process::Command` will no longer search the current directory for executables.][87704] +- [All proc-macro backward-compatibility lints are now deny-by-default.][88041] +- [proc_macro: Append .0 to unsuffixed float if it would otherwise become int token][90297] +- [Refactor weak symbols in std::sys::unix][90846]. This optimizes accesses to glibc functions, by avoiding the use of dlopen. This does not increase the [minimum expected version of glibc](https://doc.rust-lang.org/nightly/rustc/platform-support.html). However, software distributions that use symbol versions to detect library dependencies, and which take weak symbols into account in that analysis, may detect rust binaries as requiring newer versions of glibc. +- [rustdoc now rejects some unexpected semicolons in doctests][91026] + +Internal Changes +---------------- + +These changes provide no direct user facing benefits, but represent significant +improvements to the internals and overall performance of rustc +and related tools. + +- [Implement coherence checks for negative trait impls][90104] +- [Add rustc lint, warning when iterating over hashmaps][89558] +- [Optimize live point computation][90491] +- [Enable verification for 1/32nd of queries loaded from disk][90361] +- [Implement version of normalize_erasing_regions that allows for normalization failure][91255] + +[87337]: https://github.com/rust-lang/rust/pull/87337/ +[87467]: https://github.com/rust-lang/rust/pull/87467/ +[87704]: https://github.com/rust-lang/rust/pull/87704/ +[88041]: https://github.com/rust-lang/rust/pull/88041/ +[88447]: https://github.com/rust-lang/rust/pull/88447/ +[88601]: https://github.com/rust-lang/rust/pull/88601/ +[89062]: https://github.com/rust-lang/rust/pull/89062/ +[89174]: https://github.com/rust-lang/rust/pull/89174/ +[89551]: https://github.com/rust-lang/rust/pull/89551/ +[89558]: https://github.com/rust-lang/rust/pull/89558/ +[89580]: https://github.com/rust-lang/rust/pull/89580/ +[89652]: https://github.com/rust-lang/rust/pull/89652/ +[90041]: https://github.com/rust-lang/rust/pull/90041/ +[90058]: https://github.com/rust-lang/rust/pull/90058/ +[90104]: https://github.com/rust-lang/rust/pull/90104/ +[90117]: https://github.com/rust-lang/rust/pull/90117/ +[90175]: https://github.com/rust-lang/rust/pull/90175/ +[90183]: https://github.com/rust-lang/rust/pull/90183/ +[90297]: https://github.com/rust-lang/rust/pull/90297/ +[90329]: https://github.com/rust-lang/rust/pull/90329/ +[90361]: https://github.com/rust-lang/rust/pull/90361/ +[90417]: https://github.com/rust-lang/rust/pull/90417/ +[90473]: https://github.com/rust-lang/rust/pull/90473/ +[90491]: https://github.com/rust-lang/rust/pull/90491/ +[90733]: https://github.com/rust-lang/rust/pull/90733/ +[90833]: https://github.com/rust-lang/rust/pull/90833/ +[90846]: https://github.com/rust-lang/rust/pull/90846/ +[91026]: https://github.com/rust-lang/rust/pull/91026/ +[91207]: https://github.com/rust-lang/rust/pull/91207/ +[91255]: https://github.com/rust-lang/rust/pull/91255/ +[cargo/10082]: https://github.com/rust-lang/cargo/pull/10082/ +[cargo/10107]: https://github.com/rust-lang/cargo/pull/10107/ +[`Metadata::is_symlink`]: https://doc.rust-lang.org/stable/std/fs/struct.Metadata.html#method.is_symlink +[`Path::is_symlink`]: https://doc.rust-lang.org/stable/std/path/struct.Path.html#method.is_symlink +[`{integer}::saturating_div`]: https://doc.rust-lang.org/stable/std/primitive.i8.html#method.saturating_div +[`Option::unwrap_unchecked`]: https://doc.rust-lang.org/stable/std/option/enum.Option.html#method.unwrap_unchecked +[`Result::unwrap_unchecked`]: https://doc.rust-lang.org/stable/std/result/enum.Result.html#method.unwrap_unchecked +[`Result::unwrap_err_unchecked`]: https://doc.rust-lang.org/stable/std/result/enum.Result.html#method.unwrap_err_unchecked +[`File::options`]: https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.options +[`Duration::new`]: https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.new + Version 1.57.0 (2021-12-02) ========================== @@ -78,7 +212,6 @@ and related tools. [86191]: https://github.com/rust-lang/rust/pull/86191/ [87220]: https://github.com/rust-lang/rust/pull/87220/ [87260]: https://github.com/rust-lang/rust/pull/87260/ -[88243]: https://github.com/rust-lang/rust/pull/88243/ [88321]: https://github.com/rust-lang/rust/pull/88321/ [88529]: https://github.com/rust-lang/rust/pull/88529/ [88690]: https://github.com/rust-lang/rust/pull/88690/ @@ -234,8 +367,6 @@ and related tools. as well as rustdoc. [`std::os::unix::fs::chroot`]: https://doc.rust-lang.org/stable/std/os/unix/fs/fn.chroot.html -[`Iterator::intersperse`]: https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html#method.intersperse -[`Iterator::intersperse_with`]: https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html#method.intersperse [`UnsafeCell::raw_get`]: https://doc.rust-lang.org/stable/std/cell/struct.UnsafeCell.html#method.raw_get [`BufWriter::into_parts`]: https://doc.rust-lang.org/stable/std/io/struct.BufWriter.html#method.into_parts [`core::panic::{UnwindSafe, RefUnwindSafe, AssertUnwindSafe}`]: https://github.com/rust-lang/rust/pull/84662 @@ -257,12 +388,7 @@ and related tools. [rust#86183]: https://github.com/rust-lang/rust/pull/86183 [rust#87385]: https://github.com/rust-lang/rust/pull/87385 [rust#88100]: https://github.com/rust-lang/rust/pull/88100 -[rust#86860]: https://github.com/rust-lang/rust/pull/86860 -[rust#84039]: https://github.com/rust-lang/rust/pull/84039 -[rust#86492]: https://github.com/rust-lang/rust/pull/86492 -[rust#88363]: https://github.com/rust-lang/rust/pull/88363 [rust#85305]: https://github.com/rust-lang/rust/pull/85305 -[rust#87832]: https://github.com/rust-lang/rust/pull/87832 [rust#88069]: https://github.com/rust-lang/rust/pull/88069 [rust#87472]: https://github.com/rust-lang/rust/pull/87472 [rust#87699]: https://github.com/rust-lang/rust/pull/87699 @@ -273,31 +399,12 @@ and related tools. [rust#87580]: https://github.com/rust-lang/rust/pull/87580 [rust#83342]: https://github.com/rust-lang/rust/pull/83342 [rust#83093]: https://github.com/rust-lang/rust/pull/83093 -[rust#88177]: https://github.com/rust-lang/rust/pull/88177 -[rust#88548]: https://github.com/rust-lang/rust/pull/88548 -[rust#88551]: https://github.com/rust-lang/rust/pull/88551 -[rust#88299]: https://github.com/rust-lang/rust/pull/88299 -[rust#88220]: https://github.com/rust-lang/rust/pull/88220 [rust#85835]: https://github.com/rust-lang/rust/pull/85835 -[rust#86879]: https://github.com/rust-lang/rust/pull/86879 [rust#86744]: https://github.com/rust-lang/rust/pull/86744 -[rust#84662]: https://github.com/rust-lang/rust/pull/84662 -[rust#86593]: https://github.com/rust-lang/rust/pull/86593 -[rust#81050]: https://github.com/rust-lang/rust/pull/81050 [rust#81363]: https://github.com/rust-lang/rust/pull/81363 [rust#84111]: https://github.com/rust-lang/rust/pull/84111 [rust#85769]: https://github.com/rust-lang/rust/pull/85769#issuecomment-854363720 -[rust#88490]: https://github.com/rust-lang/rust/pull/88490 -[rust#88269]: https://github.com/rust-lang/rust/pull/88269 -[rust#84176]: https://github.com/rust-lang/rust/pull/84176 [rust#88399]: https://github.com/rust-lang/rust/pull/88399 -[rust#88227]: https://github.com/rust-lang/rust/pull/88227 -[rust#88200]: https://github.com/rust-lang/rust/pull/88200 -[rust#82776]: https://github.com/rust-lang/rust/pull/82776 -[rust#88077]: https://github.com/rust-lang/rust/pull/88077 -[rust#87728]: https://github.com/rust-lang/rust/pull/87728 -[rust#87050]: https://github.com/rust-lang/rust/pull/87050 -[rust#87619]: https://github.com/rust-lang/rust/pull/87619 [rust#81825]: https://github.com/rust-lang/rust/pull/81825#issuecomment-808406918 [rust#88019]: https://github.com/rust-lang/rust/pull/88019 [rust#87666]: https://github.com/rust-lang/rust/pull/87666 @@ -388,6 +495,10 @@ Compatibility Notes `Command` would cause them to be ASCII-uppercased. - [Rustdoc will now warn on using rustdoc lints that aren't prefixed with `rustdoc::`][86849] +- `RUSTFLAGS` is no longer set for build scripts. Build scripts + should use `CARGO_ENCODED_RUSTFLAGS` instead. See the + [documentation](https://doc.rust-lang.org/nightly/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts) + for more details. [86849]: https://github.com/rust-lang/rust/pull/86849 [86513]: https://github.com/rust-lang/rust/pull/86513 @@ -399,20 +510,14 @@ Compatibility Notes [86294]: https://github.com/rust-lang/rust/pull/86294 [86858]: https://github.com/rust-lang/rust/pull/86858 [86761]: https://github.com/rust-lang/rust/pull/86761 -[85769]: https://github.com/rust-lang/rust/pull/85769 [85746]: https://github.com/rust-lang/rust/pull/85746 -[85305]: https://github.com/rust-lang/rust/pull/85305 [85270]: https://github.com/rust-lang/rust/pull/85270 -[84111]: https://github.com/rust-lang/rust/pull/84111 [83918]: https://github.com/rust-lang/rust/pull/83918 [79965]: https://github.com/rust-lang/rust/pull/79965 -[87370]: https://github.com/rust-lang/rust/pull/87370 -[87298]: https://github.com/rust-lang/rust/pull/87298 [cargo/9663]: https://github.com/rust-lang/cargo/pull/9663 [cargo/9675]: https://github.com/rust-lang/cargo/pull/9675 [cargo/9550]: https://github.com/rust-lang/cargo/pull/9550 [cargo/9680]: https://github.com/rust-lang/cargo/pull/9680 -[cargo/9663]: https://github.com/rust-lang/cargo/pull/9663 [`array::map`]: https://doc.rust-lang.org/stable/std/primitive.array.html#method.map [`Bound::cloned`]: https://doc.rust-lang.org/stable/std/ops/enum.Bound.html#method.cloned [`Drain::as_str`]: https://doc.rust-lang.org/stable/std/string/struct.Drain.html#method.as_str @@ -421,7 +526,6 @@ Compatibility Notes [`MaybeUninit::assume_init_mut`]: https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#method.assume_init_mut [`MaybeUninit::assume_init_ref`]: https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#method.assume_init_ref [`MaybeUninit::write`]: https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#method.write -[`Seek::rewind`]: https://doc.rust-lang.org/stable/std/io/trait.Seek.html#method.rewind [`ops::ControlFlow`]: https://doc.rust-lang.org/stable/std/ops/enum.ControlFlow.html [`str::from_utf8_unchecked`]: https://doc.rust-lang.org/stable/std/str/fn.from_utf8_unchecked.html [`x86::_bittest`]: https://doc.rust-lang.org/stable/core/arch/x86/fn._bittest.html @@ -525,7 +629,6 @@ Compatibility Notes [85574]: https://github.com/rust-lang/rust/issues/85574 [86831]: https://github.com/rust-lang/rust/issues/86831 [86063]: https://github.com/rust-lang/rust/issues/86063 -[86831]: https://github.com/rust-lang/rust/issues/86831 [79608]: https://github.com/rust-lang/rust/pull/79608 [84988]: https://github.com/rust-lang/rust/pull/84988 [84701]: https://github.com/rust-lang/rust/pull/84701 @@ -727,7 +830,6 @@ related tools. [`Ordering::is_le`]: https://doc.rust-lang.org/std/cmp/enum.Ordering.html#method.is_le [`Ordering::is_lt`]: https://doc.rust-lang.org/std/cmp/enum.Ordering.html#method.is_lt [`Ordering::is_ne`]: https://doc.rust-lang.org/std/cmp/enum.Ordering.html#method.is_ne -[`OsStr::eq_ignore_ascii_case`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html#method.eq_ignore_ascii_case [`OsStr::is_ascii`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html#method.is_ascii [`OsStr::make_ascii_lowercase`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html#method.make_ascii_lowercase [`OsStr::make_ascii_uppercase`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html#method.make_ascii_uppercase @@ -1058,7 +1160,6 @@ Internal Only [80053]: https://github.com/rust-lang/rust/pull/80053 [79502]: https://github.com/rust-lang/rust/pull/79502 [75180]: https://github.com/rust-lang/rust/pull/75180 -[79135]: https://github.com/rust-lang/rust/pull/79135 [81521]: https://github.com/rust-lang/rust/pull/81521 [80968]: https://github.com/rust-lang/rust/pull/80968 [80959]: https://github.com/rust-lang/rust/pull/80959 @@ -1372,7 +1473,6 @@ related tools. [`slice::select_nth_unstable`]: https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.select_nth_unstable [`slice::select_nth_unstable_by`]: https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.select_nth_unstable_by [`slice::select_nth_unstable_by_key`]: https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.select_nth_unstable_by_key -[`hint::spin_loop`]: https://doc.rust-lang.org/stable/std/hint/fn.spin_loop.html [`Poll::is_ready`]: https://doc.rust-lang.org/stable/std/task/enum.Poll.html#method.is_ready [`Poll::is_pending`]: https://doc.rust-lang.org/stable/std/task/enum.Poll.html#method.is_pending [rustdoc-ws-post]: https://blog.guillaume-gomez.fr/articles/2020-11-11+New+doc+comment+handling+in+rustdoc @@ -1619,8 +1719,6 @@ Internal Only [74869]: https://github.com/rust-lang/rust/pull/74869/ [73858]: https://github.com/rust-lang/rust/pull/73858/ [75716]: https://github.com/rust-lang/rust/pull/75716/ -[75908]: https://github.com/rust-lang/rust/pull/75908/ -[75516]: https://github.com/rust-lang/rust/pull/75516/ [75560]: https://github.com/rust-lang/rust/pull/75560/ [75568]: https://github.com/rust-lang/rust/pull/75568/ [75366]: https://github.com/rust-lang/rust/pull/75366/ @@ -1635,7 +1733,6 @@ Internal Only [73583]: https://github.com/rust-lang/rust/pull/73583/ [73084]: https://github.com/rust-lang/rust/pull/73084/ [73197]: https://github.com/rust-lang/rust/pull/73197/ -[72488]: https://github.com/rust-lang/rust/pull/72488/ [cargo/8456]: https://github.com/rust-lang/cargo/pull/8456/ [cargo/8478]: https://github.com/rust-lang/cargo/pull/8478/ [cargo/8485]: https://github.com/rust-lang/cargo/pull/8485/ @@ -1646,7 +1743,6 @@ Internal Only [`RangeInclusive::is_empty`]: https://doc.rust-lang.org/nightly/std/ops/struct.RangeInclusive.html#method.is_empty [`Result::as_deref_mut`]: https://doc.rust-lang.org/nightly/std/result/enum.Result.html#method.as_deref_mut [`Result::as_deref`]: https://doc.rust-lang.org/nightly/std/result/enum.Result.html#method.as_deref -[`TypeId::of`]: https://doc.rust-lang.org/nightly/std/any/struct.TypeId.html#method.of [`Vec::leak`]: https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#method.leak [`f32::TAU`]: https://doc.rust-lang.org/nightly/std/f32/consts/constant.TAU.html [`f64::TAU`]: https://doc.rust-lang.org/nightly/std/f64/consts/constant.TAU.html @@ -2414,6 +2510,11 @@ Language - [Visibility modifiers (e.g. `pub`) are now syntactically allowed on trait items and enum variants.][66183] These are still rejected semantically, but can be seen and parsed by procedural macros and conditional compilation. +- [You can now define a Rust `extern "C"` function with `Box<T>` and use `T*` as the corresponding + type on the C side.][62514] Please see [the documentation][box-memory-layout] for more information, + including the important caveat about preferring to avoid `Box<T>` in Rust signatures for functions defined in C. + +[box-memory-layout]: https://doc.rust-lang.org/std/boxed/index.html#memory-layout Compiler -------- @@ -2488,6 +2589,7 @@ Compatibility Notes [54733]: https://github.com/rust-lang/rust/pull/54733/ [61351]: https://github.com/rust-lang/rust/pull/61351/ +[62514]: https://github.com/rust-lang/rust/pull/62514/ [67255]: https://github.com/rust-lang/rust/pull/67255/ [66661]: https://github.com/rust-lang/rust/pull/66661/ [66771]: https://github.com/rust-lang/rust/pull/66771/ @@ -2624,7 +2726,6 @@ Compatibility Notes [63803]: https://github.com/rust-lang/rust/pull/63803/ [cargo/7450]: https://github.com/rust-lang/cargo/pull/7450/ [cargo/7507]: https://github.com/rust-lang/cargo/pull/7507/ -[cargo/7525]: https://github.com/rust-lang/cargo/pull/7525/ [cargo/7333]: https://github.com/rust-lang/cargo/pull/7333/ [(rfc 2008)]: https://rust-lang.github.io/rfcs/2008-non-exhaustive.html [`f32::to_be_bytes`]: https://doc.rust-lang.org/std/primitive.f32.html#method.to_be_bytes @@ -2757,13 +2858,6 @@ Compatibility Notes [63786]: https://github.com/rust-lang/rust/pull/63786/ [63827]: https://github.com/rust-lang/rust/pull/63827/ [63834]: https://github.com/rust-lang/rust/pull/63834/ -[63927]: https://github.com/rust-lang/rust/pull/63927/ -[63933]: https://github.com/rust-lang/rust/pull/63933/ -[63934]: https://github.com/rust-lang/rust/pull/63934/ -[63938]: https://github.com/rust-lang/rust/pull/63938/ -[63940]: https://github.com/rust-lang/rust/pull/63940/ -[63941]: https://github.com/rust-lang/rust/pull/63941/ -[63945]: https://github.com/rust-lang/rust/pull/63945/ [64010]: https://github.com/rust-lang/rust/pull/64010/ [64028]: https://github.com/rust-lang/rust/pull/64028/ [64334]: https://github.com/rust-lang/rust/pull/64334/ @@ -2992,7 +3086,6 @@ Compatibility Notes [`Cell<slice>::as_slice_of_cells`]: https://doc.rust-lang.org/std/cell/struct.Cell.html#method.as_slice_of_cells [`DoubleEndedIterator::nth_back`]: https://doc.rust-lang.org/std/iter/trait.DoubleEndedIterator.html#method.nth_back [`Option::xor`]: https://doc.rust-lang.org/std/option/enum.Option.html#method.xor -[`RefCell::try_borrow_unguarded`]: https://doc.rust-lang.org/std/cell/struct.RefCell.html#method.try_borrow_unguarded [`Wrapping::reverse_bits`]: https://doc.rust-lang.org/std/num/struct.Wrapping.html#method.reverse_bits [`i128::reverse_bits`]: https://doc.rust-lang.org/std/primitive.i128.html#method.reverse_bits [`i16::reverse_bits`]: https://doc.rust-lang.org/std/primitive.i16.html#method.reverse_bits @@ -3491,7 +3584,6 @@ Compatibility Notes - [Libtest no longer creates a new thread for each test when `--test-threads=1`. It also runs the tests in deterministic order][56243] -[55982]: https://github.com/rust-lang/rust/pull/55982/ [56243]: https://github.com/rust-lang/rust/pull/56243 [56303]: https://github.com/rust-lang/rust/pull/56303/ [56351]: https://github.com/rust-lang/rust/pull/56351/ @@ -3891,7 +3983,6 @@ Cargo [52813]: https://github.com/rust-lang/rust/pull/52813/ [53218]: https://github.com/rust-lang/rust/pull/53218/ -[53555]: https://github.com/rust-lang/rust/issues/53555/ [54057]: https://github.com/rust-lang/rust/pull/54057/ [54240]: https://github.com/rust-lang/rust/pull/54240/ [54430]: https://github.com/rust-lang/rust/pull/54430/ @@ -4013,7 +4104,6 @@ Misc [53044]: https://github.com/rust-lang/rust/pull/53044/ [53165]: https://github.com/rust-lang/rust/pull/53165/ [53611]: https://github.com/rust-lang/rust/pull/53611/ -[53213]: https://github.com/rust-lang/rust/pull/53213/ [53236]: https://github.com/rust-lang/rust/pull/53236/ [53272]: https://github.com/rust-lang/rust/pull/53272/ [53370]: https://github.com/rust-lang/rust/pull/53370/ @@ -4021,7 +4111,6 @@ Misc [53774]: https://github.com/rust-lang/rust/pull/53774/ [53822]: https://github.com/rust-lang/rust/pull/53822/ [54057]: https://github.com/rust-lang/rust/pull/54057/ -[54146]: https://github.com/rust-lang/rust/pull/54146/ [54404]: https://github.com/rust-lang/rust/pull/54404/ [cargo/5877]: https://github.com/rust-lang/cargo/pull/5877/ [cargo/5878]: https://github.com/rust-lang/cargo/pull/5878/ @@ -4129,12 +4218,10 @@ Compatibility Notes [52330]: https://github.com/rust-lang/rust/pull/52330/ [52354]: https://github.com/rust-lang/rust/pull/52354/ [52402]: https://github.com/rust-lang/rust/pull/52402/ -[52103]: https://github.com/rust-lang/rust/pull/52103/ [52197]: https://github.com/rust-lang/rust/pull/52197/ [51807]: https://github.com/rust-lang/rust/pull/51807/ [51899]: https://github.com/rust-lang/rust/pull/51899/ [51912]: https://github.com/rust-lang/rust/pull/51912/ -[51511]: https://github.com/rust-lang/rust/pull/51511/ [51619]: https://github.com/rust-lang/rust/pull/51619/ [51656]: https://github.com/rust-lang/rust/pull/51656/ [51178]: https://github.com/rust-lang/rust/pull/51178/ @@ -4274,7 +4361,6 @@ Compatibility Notes [50855]: https://github.com/rust-lang/rust/pull/50855/ [51050]: https://github.com/rust-lang/rust/pull/51050/ [51196]: https://github.com/rust-lang/rust/pull/51196/ -[51200]: https://github.com/rust-lang/rust/pull/51200/ [51241]: https://github.com/rust-lang/rust/pull/51241/ [51276]: https://github.com/rust-lang/rust/pull/51276/ [51298]: https://github.com/rust-lang/rust/pull/51298/ @@ -4455,15 +4541,12 @@ Compatibility Notes [49664]: https://github.com/rust-lang/rust/pull/49664/ [49699]: https://github.com/rust-lang/rust/pull/49699/ [49707]: https://github.com/rust-lang/rust/pull/49707/ -[49719]: https://github.com/rust-lang/rust/pull/49719/ [49896]: https://github.com/rust-lang/rust/pull/49896/ [49968]: https://github.com/rust-lang/rust/pull/49968/ [50163]: https://github.com/rust-lang/rust/pull/50163 [50177]: https://github.com/rust-lang/rust/pull/50177/ [50378]: https://github.com/rust-lang/rust/pull/50378/ -[50398]: https://github.com/rust-lang/rust/pull/50398/ [50423]: https://github.com/rust-lang/rust/pull/50423/ -[cargo/5203]: https://github.com/rust-lang/cargo/pull/5203/ [cargo/5335]: https://github.com/rust-lang/cargo/pull/5335/ [cargo/5359]: https://github.com/rust-lang/cargo/pull/5359/ [cargo/5360]: https://github.com/rust-lang/cargo/pull/5360/ @@ -4665,7 +4748,6 @@ Compatibility Notes [47813]: https://github.com/rust-lang/rust/pull/47813 [48056]: https://github.com/rust-lang/rust/pull/48056 [48125]: https://github.com/rust-lang/rust/pull/48125 -[48166]: https://github.com/rust-lang/rust/pull/48166 [48235]: https://github.com/rust-lang/rust/pull/48235 [48274]: https://github.com/rust-lang/rust/pull/48274 [48281]: https://github.com/rust-lang/rust/pull/48281 @@ -4682,10 +4764,7 @@ Compatibility Notes [48978]: https://github.com/rust-lang/rust/pull/48978 [49101]: https://github.com/rust-lang/rust/pull/49101 [49109]: https://github.com/rust-lang/rust/pull/49109 -[49121]: https://github.com/rust-lang/rust/pull/49121 [49162]: https://github.com/rust-lang/rust/pull/49162 -[49184]: https://github.com/rust-lang/rust/pull/49184 -[49234]: https://github.com/rust-lang/rust/pull/49234 [49255]: https://github.com/rust-lang/rust/pull/49255 [49299]: https://github.com/rust-lang/rust/pull/49299 [49305]: https://github.com/rust-lang/rust/pull/49305 @@ -4932,7 +5011,6 @@ Compatibility Notes [44884]: https://github.com/rust-lang/rust/pull/44884 [45198]: https://github.com/rust-lang/rust/pull/45198 [45506]: https://github.com/rust-lang/rust/pull/45506 -[45904]: https://github.com/rust-lang/rust/pull/45904 [45990]: https://github.com/rust-lang/rust/pull/45990 [46012]: https://github.com/rust-lang/rust/pull/46012 [46077]: https://github.com/rust-lang/rust/pull/46077 @@ -4944,7 +5022,6 @@ Compatibility Notes [46671]: https://github.com/rust-lang/rust/pull/46671 [46713]: https://github.com/rust-lang/rust/pull/46713 [46735]: https://github.com/rust-lang/rust/pull/46735 -[46749]: https://github.com/rust-lang/rust/pull/46749 [46760]: https://github.com/rust-lang/rust/pull/46760 [46798]: https://github.com/rust-lang/rust/pull/46798 [46828]: https://github.com/rust-lang/rust/pull/46828 @@ -5115,7 +5192,6 @@ Compatibility Notes [42526]: https://github.com/rust-lang/rust/pull/42526 -[43017]: https://github.com/rust-lang/rust/pull/43017 [43716]: https://github.com/rust-lang/rust/pull/43716 [43949]: https://github.com/rust-lang/rust/pull/43949 [44015]: https://github.com/rust-lang/rust/pull/44015 @@ -5345,8 +5421,6 @@ Cargo - [Added `--no-fail-fast` flag to cargo to run all benchmarks regardless of failure.][cargo/4248] - [Changed the convention around which file is the crate root.][cargo/4259] -- [The `include`/`exclude` property in `Cargo.toml` now accepts gitignore paths - instead of glob patterns][cargo/4270]. Glob patterns are now deprecated. Compatibility Notes ------------------- @@ -5389,7 +5463,6 @@ Compatibility Notes [cargo/4229]: https://github.com/rust-lang/cargo/pull/4229 [cargo/4248]: https://github.com/rust-lang/cargo/pull/4248 [cargo/4259]: https://github.com/rust-lang/cargo/pull/4259 -[cargo/4270]: https://github.com/rust-lang/cargo/pull/4270 [`CStr::into_c_string`]: https://doc.rust-lang.org/std/ffi/struct.CStr.html#method.into_c_string [`CString::as_c_str`]: https://doc.rust-lang.org/std/ffi/struct.CString.html#method.as_c_str [`CString::into_boxed_c_str`]: https://doc.rust-lang.org/std/ffi/struct.CString.html#method.into_boxed_c_str @@ -5682,7 +5755,6 @@ Misc ---- - [rustdoc can now use pulldown-cmark with the `--enable-commonmark` flag][40338] -- [Added rust-windbg script for better debugging on Windows][39983] - [Rust now uses the official cross compiler for NetBSD][40612] - [rustdoc now accepts `#` at the start of files][40828] - [Fixed jemalloc support for musl][41168] @@ -5717,7 +5789,6 @@ Compatibility Notes [38165]: https://github.com/rust-lang/rust/pull/38165 [39799]: https://github.com/rust-lang/rust/pull/39799 [39891]: https://github.com/rust-lang/rust/pull/39891 -[39983]: https://github.com/rust-lang/rust/pull/39983 [40043]: https://github.com/rust-lang/rust/pull/40043 [40241]: https://github.com/rust-lang/rust/pull/40241 [40338]: https://github.com/rust-lang/rust/pull/40338 @@ -6013,7 +6084,6 @@ Compatibility Notes [cargo/3691]: https://github.com/rust-lang/cargo/pull/3691 [cargo/3699]: https://github.com/rust-lang/cargo/pull/3699 [cargo/3731]: https://github.com/rust-lang/cargo/pull/3731 -[mdbook]: https://crates.io/crates/mdbook [ubook]: https://doc.rust-lang.org/unstable-book/ @@ -6084,7 +6154,7 @@ Libraries * [Ctrl-Z returns from `Stdin.read()` when reading from the console on Windows][38274] * [std: Fix partial writes in `LineWriter`][38062] -* [std: Clamp max read/write sizes on Unix][38062] +* [std: Clamp max read/write sizes on Unix][38622] * [Use more specific panic message for `&str` slicing errors][38066] * [`TcpListener::set_only_v6` is deprecated][38304]. This functionality cannot be achieved in std currently. @@ -6150,7 +6220,7 @@ Compatibility Notes [38006]: https://github.com/rust-lang/rust/pull/38006 [38051]: https://github.com/rust-lang/rust/pull/38051 [38062]: https://github.com/rust-lang/rust/pull/38062 -[38062]: https://github.com/rust-lang/rust/pull/38622 +[38622]: https://github.com/rust-lang/rust/pull/38622 [38066]: https://github.com/rust-lang/rust/pull/38066 [38069]: https://github.com/rust-lang/rust/pull/38069 [38131]: https://github.com/rust-lang/rust/pull/38131 @@ -6158,7 +6228,6 @@ Compatibility Notes [38274]: https://github.com/rust-lang/rust/pull/38274 [38304]: https://github.com/rust-lang/rust/pull/38304 [38313]: https://github.com/rust-lang/rust/pull/38313 -[38314]: https://github.com/rust-lang/rust/pull/38314 [38327]: https://github.com/rust-lang/rust/pull/38327 [38401]: https://github.com/rust-lang/rust/pull/38401 [38413]: https://github.com/rust-lang/rust/pull/38413 @@ -6208,7 +6277,6 @@ Compatibility Notes [cargo/3546]: https://github.com/rust-lang/cargo/pull/3546 [cargo/3557]: https://github.com/rust-lang/cargo/pull/3557 [cargo/3604]: https://github.com/rust-lang/cargo/pull/3604 -[RFC 1623]: https://github.com/rust-lang/rfcs/blob/master/text/1623-static.md Version 1.15.1 (2017-02-09) @@ -6423,7 +6491,6 @@ Compatibility Notes [38192]: https://github.com/rust-lang/rust/pull/38192 [38279]: https://github.com/rust-lang/rust/pull/38279 [38835]: https://github.com/rust-lang/rust/pull/38835 -[RFC 1492]: https://github.com/rust-lang/rfcs/blob/master/text/1492-dotdot-in-patterns.md [RFC 1506]: https://github.com/rust-lang/rfcs/blob/master/text/1506-adt-kinds.md [RFC 1560]: https://github.com/rust-lang/rfcs/blob/master/text/1560-name-resolution.md [RFC 1681]: https://github.com/rust-lang/rfcs/blob/master/text/1681-macros-1.1.md @@ -6612,7 +6679,6 @@ Compatibility Notes [1.14wasm]: https://users.rust-lang.org/t/compiling-to-the-web-with-rust-and-emscripten/7627 [36430]: https://github.com/rust-lang/rust/pull/36430 [36595]: https://github.com/rust-lang/rust/pull/36595 -[36595]: https://github.com/rust-lang/rust/pull/36595 [36692]: https://github.com/rust-lang/rust/pull/36692 [36767]: https://github.com/rust-lang/rust/pull/36767 [36794]: https://github.com/rust-lang/rust/pull/36794 @@ -6840,7 +6906,6 @@ Compatibility Notes [34623]: https://github.com/rust-lang/rust/pull/34623 [34923]: https://github.com/rust-lang/rust/pull/34923 [34942]: https://github.com/rust-lang/rust/pull/34942 -[34982]: https://github.com/rust-lang/rust/pull/34982 [35021]: https://github.com/rust-lang/rust/pull/35021 [35048]: https://github.com/rust-lang/rust/pull/35048 [35074]: https://github.com/rust-lang/rust/pull/35074 @@ -6897,7 +6962,6 @@ Compatibility Notes [36586]: https://github.com/rust-lang/rust/pull/36586 [36592]: https://github.com/rust-lang/rust/pull/36592 [36631]: https://github.com/rust-lang/rust/pull/36631 -[36639]: https://github.com/rust-lang/rust/pull/36639 [36721]: https://github.com/rust-lang/rust/pull/36721 [36727]: https://github.com/rust-lang/rust/pull/36727 [36730]: https://github.com/rust-lang/rust/pull/36730 @@ -6929,7 +6993,6 @@ Compatibility Notes [cargo/3205]: https://github.com/rust-lang/cargo/pull/3205 [cargo/3241]: https://github.com/rust-lang/cargo/pull/3241 [cargo/3242]: https://github.com/rust-lang/cargo/pull/3242 -[rustup]: https://www.rustup.rs [`checked_abs`]: https://doc.rust-lang.org/std/primitive.i32.html#method.checked_abs [`wrapping_abs`]: https://doc.rust-lang.org/std/primitive.i32.html#method.wrapping_abs [`overflowing_abs`]: https://doc.rust-lang.org/std/primitive.i32.html#method.overflowing_abs @@ -7847,7 +7910,7 @@ Cargo targets can be specified together. [RFC 1361]. * [The environment variables `CARGO_TARGET_ROOT`, `RUSTC`, and `RUSTDOC` take precedence over the `build.target-dir`, - `build.rustc`, and `build.rustdoc` configuration values][1.8cv]. + `build.rustc`, and `build.rustdoc` configuration values][1.8cfv]. * [The child process tree is killed on Windows when Cargo is killed][1.8ck]. * [The `build.target` configuration value sets the target platform, @@ -7897,7 +7960,7 @@ Compatibility Notes [1.8ck]: https://github.com/rust-lang/cargo/pull/2370 [1.8ct]: https://github.com/rust-lang/cargo/pull/2335 [1.8cu]: https://github.com/rust-lang/rust/pull/31390 -[1.8cv]: https://github.com/rust-lang/cargo/issues/2365 +[1.8cfv]: https://github.com/rust-lang/cargo/issues/2365 [1.8cv]: https://github.com/rust-lang/rust/pull/30998 [1.8h]: https://github.com/rust-lang/rust/pull/31460 [1.8l]: https://github.com/rust-lang/rust/pull/31668 @@ -8820,13 +8883,13 @@ Misc * The compiler gained many new extended error descriptions, which can be accessed with the `--explain` flag. * The `dropck` pass, which checks that destructors can't access - destroyed values, [has been rewritten][dropck]. This fixes some + destroyed values, [has been rewritten][27261]. This fixes some soundness holes, and as such will cause some previously-compiling code to no longer build. * `rustc` now uses [LLVM to write archive files where possible][ar]. Eventually this will eliminate the compiler's dependency on the ar utility. -* Rust has [preliminary support for i686 FreeBSD][fb] (it has long +* Rust has [preliminary support for i686 FreeBSD][26959] (it has long supported FreeBSD on x86_64). * The [`unused_mut`][lum], [`unconditional_recursion`][lur], [`improper_ctypes`][lic], and [`negate_unsigned`][lnu] lints are @@ -8865,7 +8928,7 @@ Misc [ar]: https://github.com/rust-lang/rust/pull/26926 [b14]: https://static.rust-lang.org/dist/rust-beta-x86_64-pc-windows-msvc.msi [dms]: https://github.com/rust-lang/rust/pull/26241 -[dropck]: https://github.com/rust-lang/rust/pull/27261 +[27261]: https://github.com/rust-lang/rust/pull/27261 [dropckrfc]: https://github.com/rust-lang/rfcs/blob/master/text/0769-sound-generic-drop.md [ds]: https://github.com/rust-lang/rust/pull/26818 [dst1]: http://doc.rust-lang.org/nightly/std/mem/fn.size_of_val.html @@ -8873,9 +8936,8 @@ Misc [dst3]: https://github.com/rust-lang/rust/pull/27351 [e]: https://github.com/rust-lang/rust/pull/24793 [f]: https://github.com/rust-lang/rust/pull/26588 -[fb]: https://github.com/rust-lang/rust/pull/26959 +[26959]: https://github.com/rust-lang/rust/pull/26959 [fl]: https://github.com/rust-lang/rust-installer/pull/41 -[hs]: http://doc.rust-lang.org/nightly/std/hash/trait.Hash.html#method.hash_slice [ie]: http://doc.rust-lang.org/nightly/std/io/struct.Error.html [iec]: http://doc.rust-lang.org/nightly/std/io/struct.Error.html#method.cause [iegm]: http://doc.rust-lang.org/nightly/std/io/struct.Error.html#method.get_mut @@ -9146,7 +9208,7 @@ Misc to rustc. * [Android executables are always position independent][pie]. * [The `drop_with_repr_extern` lint warns about mixing `repr(C)` - with `Drop`][drop]. + with `Drop`][24935]. [`str::split_whitespace`]: https://doc.rust-lang.org/nightly/std/primitive.str.html#method.split_whitespace [`FromRawFd`]: https://doc.rust-lang.org/nightly/std/os/unix/io/trait.FromRawFd.html @@ -9176,7 +9238,7 @@ Misc [`BinaryHeap`]: https://doc.rust-lang.org/nightly/std/collections/struct.BinaryHeap.html [ll]: https://github.com/rust-lang/rust/pull/26022 [`split_off`]: https://doc.rust-lang.org/nightly/collections/linked_list/struct.LinkedList.html#method.split_off -[drop]: https://github.com/rust-lang/rust/pull/24935 +[24935]: https://github.com/rust-lang/rust/pull/24935 Version 1.0.0 (2015-05-15) ======================== @@ -9229,7 +9291,7 @@ Language property: generic code cannot behave differently for different type arguments except in minor ways. * The `unsafe_destructor` feature is now deprecated in favor of the - [new `dropck`][dropck]. This change is a major reduction in unsafe + [new `dropck`][rfc769]. This change is a major reduction in unsafe code. Libraries @@ -9237,7 +9299,7 @@ Libraries * The `thread_local` module [has been renamed to `std::thread`][th]. * The methods of `IteratorExt` [have been moved to the `Iterator` - trait itself][ie]. + trait itself][23300]. * Several traits that implement Rust's conventions for type conversions, `AsMut`, `AsRef`, `From`, and `Into` have been [centralized in the `std::convert` module][con]. @@ -9256,7 +9318,7 @@ Libraries * [In method resolution, object methods are resolved before inherent methods][meth]. * [`String::from_str` has been deprecated in favor of the `From` impl, - `String::from`][sf]. + `String::from`][24517]. * [`io::Error` implements `Sync`][ios]. * [The `words` method on `&str` has been replaced with `split_whitespace`][sw], to avoid answering the tricky question, 'what is @@ -9304,7 +9366,7 @@ Misc [con]: https://github.com/rust-lang/rust/pull/23875 [cr]: https://github.com/rust-lang/rust/pull/23419 [fe]: https://github.com/rust-lang/rust/pull/23879 -[ie]: https://github.com/rust-lang/rust/pull/23300 +[23300]: https://github.com/rust-lang/rust/pull/23300 [inv]: https://github.com/rust-lang/rust/pull/23938 [ios]: https://github.com/rust-lang/rust/pull/24133 [lex]: https://github.com/rust-lang/rfcs/blob/master/text/0879-small-base-lexing.md @@ -9312,7 +9374,7 @@ Misc [meth]: https://github.com/rust-lang/rust/pull/24056 [pat]: https://github.com/rust-lang/rfcs/blob/master/text/0528-string-patterns.md [po]: https://github.com/rust-lang/rust/pull/24270 -[sf]: https://github.com/rust-lang/rust/pull/24517 +[24517]: https://github.com/rust-lang/rust/pull/24517 [slp]: https://github.com/rust-lang/rust/pull/23949 [spl]: https://github.com/rust-lang/rfcs/blob/master/text/0979-align-splitn-with-other-languages.md [sw]: https://github.com/rust-lang/rfcs/blob/master/text/1054-str-words.md @@ -9330,7 +9392,7 @@ Misc [conversion]: https://github.com/rust-lang/rfcs/pull/529 [num-traits]: https://github.com/rust-lang/rust/pull/23549 [index-value]: https://github.com/rust-lang/rust/pull/23601 -[dropck]: https://github.com/rust-lang/rfcs/pull/769 +[rfc769]: https://github.com/rust-lang/rfcs/pull/769 [ci-compare]: https://gist.github.com/brson/a30a77836fbec057cbee [fn-inherit]: https://github.com/rust-lang/rust/pull/23282 [fn-blanket]: https://github.com/rust-lang/rust/pull/23895 diff --git a/compiler/rustc_ast/src/ast_like.rs b/compiler/rustc_ast/src/ast_like.rs index b9c397974a1..9a24158ba35 100644 --- a/compiler/rustc_ast/src/ast_like.rs +++ b/compiler/rustc_ast/src/ast_like.rs @@ -6,12 +6,13 @@ use super::{AssocItem, Expr, ForeignItem, Item, Local, MacCallStmt}; use super::{AttrItem, AttrKind, Block, Pat, Path, Ty, Visibility}; use super::{AttrVec, Attribute, Stmt, StmtKind}; -use std::fmt::Debug; +use std::fmt; +use std::marker::PhantomData; /// An `AstLike` represents an AST node (or some wrapper around /// and AST node) which stores some combination of attributes /// and tokens. -pub trait AstLike: Sized + Debug { +pub trait AstLike: Sized + fmt::Debug { /// This is `true` if this `AstLike` might support 'custom' (proc-macro) inner /// attributes. Attributes like `#![cfg]` and `#![cfg_attr]` are not /// considered 'custom' attributes @@ -285,3 +286,37 @@ derive_has_attrs_no_tokens! { derive_has_tokens_no_attrs! { Ty, Block, AttrItem, Pat, Path, Visibility } + +/// A newtype around an `AstLike` node that implements `AstLike` itself. +pub struct AstLikeWrapper<Wrapped, Tag> { + pub wrapped: Wrapped, + pub tag: PhantomData<Tag>, +} + +impl<Wrapped, Tag> AstLikeWrapper<Wrapped, Tag> { + pub fn new(wrapped: Wrapped, _tag: Tag) -> AstLikeWrapper<Wrapped, Tag> { + AstLikeWrapper { wrapped, tag: Default::default() } + } +} + +impl<Wrapped: fmt::Debug, Tag> fmt::Debug for AstLikeWrapper<Wrapped, Tag> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("AstLikeWrapper") + .field("wrapped", &self.wrapped) + .field("tag", &self.tag) + .finish() + } +} + +impl<Wrapped: AstLike, Tag> AstLike for AstLikeWrapper<Wrapped, Tag> { + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = Wrapped::SUPPORTS_CUSTOM_INNER_ATTRS; + fn attrs(&self) -> &[Attribute] { + self.wrapped.attrs() + } + fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) { + self.wrapped.visit_attrs(f) + } + fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> { + self.wrapped.tokens_mut() + } +} diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index ff3b501a0bd..84fe9ad2672 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -41,7 +41,7 @@ pub mod tokenstream; pub mod visit; pub use self::ast::*; -pub use self::ast_like::AstLike; +pub use self::ast_like::{AstLike, AstLikeWrapper}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; diff --git a/compiler/rustc_ast/src/util/comments.rs b/compiler/rustc_ast/src/util/comments.rs index 80a06fa5943..0a391123dd3 100644 --- a/compiler/rustc_ast/src/util/comments.rs +++ b/compiler/rustc_ast/src/util/comments.rs @@ -34,18 +34,11 @@ pub fn beautify_doc_string(data: Symbol) -> Symbol { i += 1; } - while i < j && lines[i].trim().is_empty() { - i += 1; - } // like the first, a last line of all stars should be omitted if j > i && !lines[j - 1].is_empty() && lines[j - 1].chars().all(|c| c == '*') { j -= 1; } - while j > i && lines[j - 1].trim().is_empty() { - j -= 1; - } - if i != 0 || j != lines.len() { Some((i, j)) } else { None } } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 75f384405bb..885537a212f 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -11,7 +11,7 @@ use rustc_hir::def::Res; use rustc_hir::definitions::DefPathData; use rustc_span::hygiene::ExpnId; use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned}; -use rustc_span::symbol::{sym, Ident, Symbol}; +use rustc_span::symbol::{sym, Ident}; use rustc_span::DUMMY_SP; impl<'hir> LoweringContext<'_, 'hir> { @@ -1204,11 +1204,13 @@ impl<'hir> LoweringContext<'_, 'hir> { }; let fields = self.arena.alloc_from_iter( - e1.iter().map(|e| ("start", e)).chain(e2.iter().map(|e| ("end", e))).map(|(s, e)| { - let expr = self.lower_expr(&e); - let ident = Ident::new(Symbol::intern(s), self.lower_span(e.span)); - self.expr_field(ident, expr, e.span) - }), + e1.iter().map(|e| (sym::start, e)).chain(e2.iter().map(|e| (sym::end, e))).map( + |(s, e)| { + let expr = self.lower_expr(&e); + let ident = Ident::new(s, self.lower_span(e.span)); + self.expr_field(ident, expr, e.span) + }, + ), ); hir::ExprKind::Struct( diff --git a/compiler/rustc_ast_pretty/src/pprust/mod.rs b/compiler/rustc_ast_pretty/src/pprust/mod.rs index 4b5703a429e..ac9e7d06c4e 100644 --- a/compiler/rustc_ast_pretty/src/pprust/mod.rs +++ b/compiler/rustc_ast_pretty/src/pprust/mod.rs @@ -73,11 +73,11 @@ pub fn attribute_to_string(attr: &ast::Attribute) -> String { } pub fn to_string(f: impl FnOnce(&mut State<'_>)) -> String { - State::new().to_string(f) + State::to_string(f) } pub fn crate_to_string_for_macros(krate: &ast::Crate) -> String { - State::new().to_string(|s| { + State::to_string(|s| { s.print_inner_attributes(&krate.attrs); for item in &krate.items { s.print_item(item); diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 3267945f427..fa9a20f2e03 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -211,7 +211,7 @@ pub fn literal_to_string(lit: token::Lit) -> String { } fn visibility_qualified(vis: &ast::Visibility, s: &str) -> String { - format!("{}{}", State::new().to_string(|s| s.print_visibility(vis)), s) + format!("{}{}", State::to_string(|s| s.print_visibility(vis)), s) } impl std::ops::Deref for State<'_> { @@ -793,55 +793,55 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere } fn ty_to_string(&self, ty: &ast::Ty) -> String { - self.to_string(|s| s.print_type(ty)) + Self::to_string(|s| s.print_type(ty)) } fn bounds_to_string(&self, bounds: &[ast::GenericBound]) -> String { - self.to_string(|s| s.print_type_bounds("", bounds)) + Self::to_string(|s| s.print_type_bounds("", bounds)) } fn pat_to_string(&self, pat: &ast::Pat) -> String { - self.to_string(|s| s.print_pat(pat)) + Self::to_string(|s| s.print_pat(pat)) } fn expr_to_string(&self, e: &ast::Expr) -> String { - self.to_string(|s| s.print_expr(e)) + Self::to_string(|s| s.print_expr(e)) } fn tt_to_string(&self, tt: &TokenTree) -> String { - self.to_string(|s| s.print_tt(tt, false)) + Self::to_string(|s| s.print_tt(tt, false)) } fn tts_to_string(&self, tokens: &TokenStream) -> String { - self.to_string(|s| s.print_tts(tokens, false)) + Self::to_string(|s| s.print_tts(tokens, false)) } fn stmt_to_string(&self, stmt: &ast::Stmt) -> String { - self.to_string(|s| s.print_stmt(stmt)) + Self::to_string(|s| s.print_stmt(stmt)) } fn item_to_string(&self, i: &ast::Item) -> String { - self.to_string(|s| s.print_item(i)) + Self::to_string(|s| s.print_item(i)) } fn generic_params_to_string(&self, generic_params: &[ast::GenericParam]) -> String { - self.to_string(|s| s.print_generic_params(generic_params)) + Self::to_string(|s| s.print_generic_params(generic_params)) } fn path_to_string(&self, p: &ast::Path) -> String { - self.to_string(|s| s.print_path(p, false, 0)) + Self::to_string(|s| s.print_path(p, false, 0)) } fn path_segment_to_string(&self, p: &ast::PathSegment) -> String { - self.to_string(|s| s.print_path_segment(p, false)) + Self::to_string(|s| s.print_path_segment(p, false)) } fn vis_to_string(&self, v: &ast::Visibility) -> String { - self.to_string(|s| s.print_visibility(v)) + Self::to_string(|s| s.print_visibility(v)) } fn block_to_string(&self, blk: &ast::Block) -> String { - self.to_string(|s| { + Self::to_string(|s| { // Containing cbox, will be closed by `print_block` at `}`. s.cbox(INDENT_UNIT); // Head-ibox, will be closed by `print_block` after `{`. @@ -851,22 +851,22 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere } fn meta_list_item_to_string(&self, li: &ast::NestedMetaItem) -> String { - self.to_string(|s| s.print_meta_list_item(li)) + Self::to_string(|s| s.print_meta_list_item(li)) } fn attr_item_to_string(&self, ai: &ast::AttrItem) -> String { - self.to_string(|s| s.print_attr_item(ai, ai.path.span)) + Self::to_string(|s| s.print_attr_item(ai, ai.path.span)) } fn attribute_to_string(&self, attr: &ast::Attribute) -> String { - self.to_string(|s| s.print_attribute(attr)) + Self::to_string(|s| s.print_attribute(attr)) } fn param_to_string(&self, arg: &ast::Param) -> String { - self.to_string(|s| s.print_param(arg, false)) + Self::to_string(|s| s.print_param(arg, false)) } - fn to_string(&self, f: impl FnOnce(&mut State<'_>)) -> String { + fn to_string(f: impl FnOnce(&mut State<'_>)) -> String { let mut printer = State::new(); f(&mut printer); printer.s.eof() @@ -1202,7 +1202,7 @@ impl<'a> State<'a> { ); } ast::ItemKind::Mod(unsafety, ref mod_kind) => { - self.head(self.to_string(|s| { + self.head(Self::to_string(|s| { s.print_visibility(&item.vis); s.print_unsafety(unsafety); s.word("mod"); @@ -1228,7 +1228,7 @@ impl<'a> State<'a> { } } ast::ItemKind::ForeignMod(ref nmod) => { - self.head(self.to_string(|s| { + self.head(Self::to_string(|s| { s.print_unsafety(nmod.unsafety); s.word("extern"); })); @@ -1450,7 +1450,7 @@ impl<'a> State<'a> { ast::CrateSugar::JustCrate => self.word_nbsp("crate"), }, ast::VisibilityKind::Restricted { ref path, .. } => { - let path = self.to_string(|s| s.print_path(path, false, 0)); + let path = Self::to_string(|s| s.print_path(path, false, 0)); if path == "self" || path == "super" { self.word_nbsp(format!("pub({})", path)) } else { diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index e2eb125981f..84acfbf941d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -372,7 +372,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } else { def.non_enum_variant() }; - variant.fields[field.index()].ident.to_string() + variant.fields[field.index()].name.to_string() } ty::Tuple(_) => field.index().to_string(), ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => { diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index b5dad5ccdea..02935af8314 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -706,13 +706,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { &origin_projection, ) { match captured_place.info.capture_kind { - ty::UpvarCapture::ByRef(ty::UpvarBorrow { - kind: ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow, - .. - }) => { + ty::UpvarCapture::ByRef( + ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow, + ) => { capture_reason = format!("mutable borrow of `{}`", upvar); } - ty::UpvarCapture::ByValue(_) => { + ty::UpvarCapture::ByValue => { capture_reason = format!("possible mutation of `{}`", upvar); } _ => bug!("upvar `{}` borrowed, but not mutably", upvar), diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index fe34d6e7ca9..7e961e1e750 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -186,7 +186,7 @@ fn do_mir_borrowck<'a, 'tcx>( .map(|captured_place| { let capture = captured_place.info.capture_kind; let by_ref = match capture { - ty::UpvarCapture::ByValue(_) => false, + ty::UpvarCapture::ByValue => false, ty::UpvarCapture::ByRef(..) => true, }; Upvar { place: captured_place.clone(), by_ref } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 1f745f977d4..87b0a887d1c 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -312,6 +312,7 @@ fn translate_outlives_facts(typeck: &mut TypeChecker<'_, '_>) { } } +#[track_caller] fn mirbug(tcx: TyCtxt<'_>, span: Span, msg: &str) { // We sometimes see MIR failures (notably predicate failures) due to // the fact that we check rvalue sized predicates here. So use `delay_span_bug` diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 1a93b9be99e..caf8ac77df1 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -16,13 +16,13 @@ use rustc_target::asm::InlineAsmArch; use smallvec::smallvec; pub struct AsmArgs { - templates: Vec<P<ast::Expr>>, - operands: Vec<(ast::InlineAsmOperand, Span)>, + pub templates: Vec<P<ast::Expr>>, + pub operands: Vec<(ast::InlineAsmOperand, Span)>, named_args: FxHashMap<Symbol, usize>, reg_args: FxHashSet<usize>, - clobber_abis: Vec<(Symbol, Span)>, + pub clobber_abis: Vec<(Symbol, Span)>, options: ast::InlineAsmOptions, - options_spans: Vec<Span>, + pub options_spans: Vec<Span>, } fn parse_args<'a>( diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs index 638b025be22..8e203b8cfa0 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs @@ -174,7 +174,7 @@ impl<'tcx> DebugContext<'tcx> { field_entry.set( gimli::DW_AT_name, - AttributeValue::String(field_def.ident.as_str().to_string().into_bytes()), + AttributeValue::String(field_def.name.as_str().to_string().into_bytes()), ); field_entry.set( gimli::DW_AT_data_member_location, diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs index 9c39c8f91a1..281e49fa8a3 100644 --- a/compiler/rustc_codegen_gcc/src/type_of.rs +++ b/compiler/rustc_codegen_gcc/src/type_of.rs @@ -57,7 +57,7 @@ pub fn uncached_gcc_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, layout: TyAndLa (layout.ty.kind(), &layout.variants) { if def.is_enum() && !def.variants.is_empty() { - write!(&mut name, "::{}", def.variants[index].ident).unwrap(); + write!(&mut name, "::{}", def.variants[index].name).unwrap(); } } if let (&ty::Generator(_, _, _), &Variants::Single { index }) = diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index e0af5653753..32f18419753 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -5,12 +5,13 @@ use crate::llvm; use llvm::coverageinfo::CounterMappingRegion; use rustc_codegen_ssa::coverageinfo::map::{Counter, CounterExpression}; use rustc_codegen_ssa::traits::{ConstMethods, CoverageInfoMethods}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; -use rustc_hir::def_id::{DefId, DefIdSet}; +use rustc_data_structures::fx::FxIndexSet; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::DefIdSet; use rustc_llvm::RustString; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::coverage::CodeRegion; use rustc_middle::ty::TyCtxt; -use rustc_span::Symbol; use std::ffi::CString; @@ -46,7 +47,7 @@ pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) { // functions exist. Generate synthetic functions with a (required) single counter, and add the // MIR `Coverage` code regions to the `function_coverage_map`, before calling // `ctx.take_function_coverage_map()`. - if !tcx.sess.instrument_coverage_except_unused_functions() { + if cx.codegen_unit.is_code_coverage_dead_code_cgu() { add_unused_functions(cx); } @@ -271,26 +272,35 @@ fn save_function_record( /// `DefId`s (`tcx` query `mir_keys`) minus the codegenned `DefId`s (`tcx` query /// `codegened_and_inlined_items`). /// -/// *HOWEVER* the codegenned `DefId`s are partitioned across multiple `CodegenUnit`s (CGUs), and -/// this function is processing a `function_coverage_map` for the functions (`Instance`/`DefId`) -/// allocated to only one of those CGUs. We must NOT inject any unused functions's `CodeRegion`s -/// more than once, so we have to pick a CGUs `function_coverage_map` into which the unused -/// function will be inserted. +/// These unused functions are then codegen'd in one of the CGUs which is marked as the +/// "code coverage dead code cgu" during the partitioning process. This prevents us from generating +/// code regions for the same function more than once which can lead to linker errors regarding +/// duplicate symbols. fn add_unused_functions<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) { - let tcx = cx.tcx; + assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu()); - // FIXME(#79622): Can this solution be simplified and/or improved? Are there other sources - // of compiler state data that might help (or better sources that could be exposed, but - // aren't yet)? + let tcx = cx.tcx; let ignore_unused_generics = tcx.sess.instrument_coverage_except_unused_generics(); - let all_def_ids: DefIdSet = tcx + let eligible_def_ids: DefIdSet = tcx .mir_keys(()) .iter() .filter_map(|local_def_id| { let def_id = local_def_id.to_def_id(); - if ignore_unused_generics && tcx.generics_of(def_id).requires_monomorphization(tcx) { + let kind = tcx.def_kind(def_id); + // `mir_keys` will give us `DefId`s for all kinds of things, not + // just "functions", like consts, statics, etc. Filter those out. + // If `ignore_unused_generics` was specified, filter out any + // generic functions from consideration as well. + if !matches!( + kind, + DefKind::Fn | DefKind::AssocFn | DefKind::Closure | DefKind::Generator + ) { + return None; + } else if ignore_unused_generics + && tcx.generics_of(def_id).requires_monomorphization(tcx) + { return None; } Some(local_def_id.to_def_id()) @@ -299,79 +309,17 @@ fn add_unused_functions<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) { let codegenned_def_ids = tcx.codegened_and_inlined_items(()); - let mut unused_def_ids_by_file: FxHashMap<Symbol, Vec<DefId>> = FxHashMap::default(); - for &non_codegenned_def_id in all_def_ids.difference(codegenned_def_ids) { - // Make sure the non-codegenned (unused) function has at least one MIR - // `Coverage` statement with a code region, and return its file name. - if let Some(non_codegenned_file_name) = tcx.covered_file_name(non_codegenned_def_id) { - let def_ids = - unused_def_ids_by_file.entry(*non_codegenned_file_name).or_insert_with(Vec::new); - def_ids.push(non_codegenned_def_id); - } - } + for &non_codegenned_def_id in eligible_def_ids.difference(codegenned_def_ids) { + let codegen_fn_attrs = tcx.codegen_fn_attrs(non_codegenned_def_id); - if unused_def_ids_by_file.is_empty() { - // There are no unused functions with file names to add (in any CGU) - return; - } - - // Each `CodegenUnit` (CGU) has its own function_coverage_map, and generates a specific binary - // with its own coverage map. - // - // Each covered function `Instance` can be included in only one coverage map, produced from a - // specific function_coverage_map, from a specific CGU. - // - // Since unused functions did not generate code, they are not associated with any CGU yet. - // - // To avoid injecting the unused functions in multiple coverage maps (for multiple CGUs) - // determine which function_coverage_map has the responsibility for publishing unreachable - // coverage, based on file name: For each unused function, find the CGU that generates the - // first function (based on sorted `DefId`) from the same file. - // - // Add a new `FunctionCoverage` to the `function_coverage_map`, with unreachable code regions - // for each region in it's MIR. - - // Convert the `HashSet` of `codegenned_def_ids` to a sortable vector, and sort them. - let mut sorted_codegenned_def_ids: Vec<DefId> = codegenned_def_ids.iter().copied().collect(); - sorted_codegenned_def_ids.sort_unstable(); - - let mut first_covered_def_id_by_file: FxHashMap<Symbol, DefId> = FxHashMap::default(); - for &def_id in sorted_codegenned_def_ids.iter() { - if let Some(covered_file_name) = tcx.covered_file_name(def_id) { - // Only add files known to have unused functions - if unused_def_ids_by_file.contains_key(covered_file_name) { - first_covered_def_id_by_file.entry(*covered_file_name).or_insert(def_id); - } + // If a function is marked `#[no_coverage]`, then skip generating a + // dead code stub for it. + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_COVERAGE) { + debug!("skipping unused fn marked #[no_coverage]: {:?}", non_codegenned_def_id); + continue; } - } - - // Get the set of def_ids with coverage regions, known by *this* CoverageContext. - let cgu_covered_def_ids: DefIdSet = match cx.coverage_context() { - Some(ctx) => ctx - .function_coverage_map - .borrow() - .keys() - .map(|&instance| instance.def.def_id()) - .collect(), - None => return, - }; - let cgu_covered_files: FxHashSet<Symbol> = first_covered_def_id_by_file - .iter() - .filter_map( - |(&file_name, def_id)| { - if cgu_covered_def_ids.contains(def_id) { Some(file_name) } else { None } - }, - ) - .collect(); - - // For each file for which this CGU is responsible for adding unused function coverage, - // get the `def_id`s for each unused function (if any), define a synthetic function with a - // single LLVM coverage counter, and add the function's coverage `CodeRegion`s. to the - // function_coverage_map. - for covered_file_name in cgu_covered_files { - for def_id in unused_def_ids_by_file.remove(&covered_file_name).into_iter().flatten() { - cx.define_unused_fn(def_id); - } + debug!("generating unused fn: {:?}", non_codegenned_def_id); + cx.define_unused_fn(non_codegenned_def_id); } } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index e8d35cf5697..5c02e3d0fa7 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -18,6 +18,7 @@ use crate::llvm::debuginfo::{ use crate::value::Value; use cstr::cstr; +use rustc_codegen_ssa::debuginfo::type_names::cpp_like_debuginfo; use rustc_codegen_ssa::traits::*; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; @@ -933,16 +934,16 @@ fn basic_type_metadata<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'l // When targeting MSVC, emit MSVC style type names for compatibility with // .natvis visualizers (and perhaps other existing native debuggers?) - let msvc_like_names = cx.tcx.sess.target.is_like_msvc; + let cpp_like_debuginfo = cpp_like_debuginfo(cx.tcx); let (name, encoding) = match t.kind() { ty::Never => ("!", DW_ATE_unsigned), ty::Tuple(elements) if elements.is_empty() => ("()", DW_ATE_unsigned), ty::Bool => ("bool", DW_ATE_boolean), ty::Char => ("char", DW_ATE_unsigned_char), - ty::Int(int_ty) if msvc_like_names => (int_ty.msvc_basic_name(), DW_ATE_signed), - ty::Uint(uint_ty) if msvc_like_names => (uint_ty.msvc_basic_name(), DW_ATE_unsigned), - ty::Float(float_ty) if msvc_like_names => (float_ty.msvc_basic_name(), DW_ATE_float), + ty::Int(int_ty) if cpp_like_debuginfo => (int_ty.msvc_basic_name(), DW_ATE_signed), + ty::Uint(uint_ty) if cpp_like_debuginfo => (uint_ty.msvc_basic_name(), DW_ATE_unsigned), + ty::Float(float_ty) if cpp_like_debuginfo => (float_ty.msvc_basic_name(), DW_ATE_float), ty::Int(int_ty) => (int_ty.name_str(), DW_ATE_signed), ty::Uint(uint_ty) => (uint_ty.name_str(), DW_ATE_unsigned), ty::Float(float_ty) => (float_ty.name_str(), DW_ATE_float), @@ -959,7 +960,7 @@ fn basic_type_metadata<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'l ) }; - if !msvc_like_names { + if !cpp_like_debuginfo { return ty_metadata; } @@ -1299,7 +1300,7 @@ impl<'tcx> StructMemberDescriptionFactory<'tcx> { let name = if self.variant.ctor_kind == CtorKind::Fn { format!("__{}", i) } else { - f.ident.to_string() + f.name.to_string() }; let field = layout.field(cx, i); MemberDescription { @@ -1479,7 +1480,7 @@ impl<'tcx> UnionMemberDescriptionFactory<'tcx> { .map(|(i, f)| { let field = self.layout.field(cx, i); MemberDescription { - name: f.ident.to_string(), + name: f.name.to_string(), type_metadata: type_metadata(cx, field.ty, self.span), offset: Size::ZERO, size: field.size, @@ -1525,13 +1526,6 @@ fn prepare_union_metadata<'ll, 'tcx>( // Enums //=----------------------------------------------------------------------------- -/// DWARF variant support is only available starting in LLVM 8, but -/// on MSVC we have to use the fallback mode, because LLVM doesn't -/// lower variant parts to PDB. -fn use_enum_fallback(cx: &CodegenCx<'_, '_>) -> bool { - cx.sess().target.is_like_msvc -} - // FIXME(eddyb) maybe precompute this? Right now it's computed once // per generator monomorphization, but it doesn't depend on substs. fn generator_layout_and_saved_local_names<'tcx>( @@ -1606,7 +1600,10 @@ impl<'ll, 'tcx> EnumMemberDescriptionFactory<'ll, 'tcx> { _ => bug!(), }; - let fallback = use_enum_fallback(cx); + // While LLVM supports generating debuginfo for variant types (enums), it doesn't support + // lowering that debuginfo to CodeView records for msvc targets. So if we are targeting + // msvc, then we need to use a different, fallback encoding of the debuginfo. + let fallback = cpp_like_debuginfo(cx.tcx); // This will always find the metadata in the type map. let self_metadata = type_metadata(cx, self.enum_type, self.span); @@ -1953,7 +1950,7 @@ enum VariantInfo<'a, 'tcx> { impl<'tcx> VariantInfo<'_, 'tcx> { fn map_struct_name<R>(&self, f: impl FnOnce(&str) -> R) -> R { match self { - VariantInfo::Adt(variant) => f(variant.ident.as_str()), + VariantInfo::Adt(variant) => f(variant.name.as_str()), VariantInfo::Generator { variant_index, .. } => { f(&GeneratorSubsts::variant_name(*variant_index)) } @@ -1962,7 +1959,7 @@ impl<'tcx> VariantInfo<'_, 'tcx> { fn variant_name(&self) -> String { match self { - VariantInfo::Adt(variant) => variant.ident.to_string(), + VariantInfo::Adt(variant) => variant.name.to_string(), VariantInfo::Generator { variant_index, .. } => { // Since GDB currently prints out the raw discriminant along // with every variant, make each variant name be just the value @@ -1976,7 +1973,7 @@ impl<'tcx> VariantInfo<'_, 'tcx> { fn field_name(&self, i: usize) -> String { let field_name = match *self { VariantInfo::Adt(variant) if variant.ctor_kind != CtorKind::Fn => { - Some(variant.fields[i].ident.name) + Some(variant.fields[i].name) } VariantInfo::Generator { generator_layout, @@ -2066,7 +2063,7 @@ fn prepare_enum_metadata<'ll, 'tcx>( let enumerators_metadata: Vec<_> = match enum_type.kind() { ty::Adt(def, _) => iter::zip(def.discriminants(tcx), &def.variants) .map(|((_, discr), v)| { - let name = v.ident.as_str(); + let name = v.name.as_str(); let is_unsigned = match discr.ty.kind() { ty::Int(_) => false, ty::Uint(_) => true, @@ -2159,7 +2156,10 @@ fn prepare_enum_metadata<'ll, 'tcx>( return FinalMetadata(discriminant_type_metadata(tag.value)); } - if use_enum_fallback(cx) { + // While LLVM supports generating debuginfo for variant types (enums), it doesn't support + // lowering that debuginfo to CodeView records for msvc targets. So if we are targeting + // msvc, then we need to use a different encoding of the debuginfo. + if cpp_like_debuginfo(tcx) { let discriminant_type_metadata = match layout.variants { Variants::Single { .. } => None, Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, tag, .. } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 07d49b6e729..1e795efa2e1 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -791,7 +791,7 @@ fn get_rust_try_fn<'ll, 'tcx>( ))); // `unsafe fn(unsafe fn(*mut i8) -> (), *mut i8, unsafe fn(*mut i8, *mut i8) -> ()) -> i32` let rust_fn_sig = ty::Binder::dummy(cx.tcx.mk_fn_sig( - vec![try_fn_ty, i8p, catch_fn_ty].into_iter(), + [try_fn_ty, i8p, catch_fn_ty].into_iter(), tcx.types.i32, false, hir::Unsafety::Unsafe, diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index f8c919ec2aa..81d0603bc52 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -49,7 +49,7 @@ fn uncached_llvm_type<'a, 'tcx>( (layout.ty.kind(), &layout.variants) { if def.is_enum() && !def.variants.is_empty() { - write!(&mut name, "::{}", def.variants[index].ident).unwrap(); + write!(&mut name, "::{}", def.variants[index].name).unwrap(); } } if let (&ty::Generator(_, _, _), &Variants::Single { index }) = diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 5c13dfdc1b5..6c6ee363ea3 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -41,6 +41,6 @@ rustc_target = { path = "../rustc_target" } rustc_session = { path = "../rustc_session" } [dependencies.object] -version = "0.26.2" +version = "0.28.0" default-features = false features = ["read_core", "elf", "macho", "pe", "unaligned", "archive", "write"] diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 79c24f0f172..6849533abc0 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -95,7 +95,7 @@ fn search_for_metadata<'a>( .map_err(|e| format!("failed to read {} section in '{}': {}", section, path.display(), e)) } -fn create_object_file(sess: &Session) -> Option<write::Object> { +fn create_object_file(sess: &Session) -> Option<write::Object<'static>> { let endianness = match sess.target.options.endian { Endian::Little => Endianness::Little, Endian::Big => Endianness::Big, @@ -135,12 +135,24 @@ fn create_object_file(sess: &Session) -> Option<write::Object> { Architecture::Mips => { // copied from `mipsel-linux-gnu-gcc foo.c -c` and // inspecting the resulting `e_flags` field. - let e_flags = elf::EF_MIPS_ARCH_32R2 | elf::EF_MIPS_CPIC | elf::EF_MIPS_PIC; + let e_flags = elf::EF_MIPS_CPIC + | elf::EF_MIPS_PIC + | if sess.target.options.cpu.contains("r6") { + elf::EF_MIPS_ARCH_32R6 | elf::EF_MIPS_NAN2008 + } else { + elf::EF_MIPS_ARCH_32R2 + }; file.flags = FileFlags::Elf { e_flags }; } Architecture::Mips64 => { // copied from `mips64el-linux-gnuabi64-gcc foo.c -c` - let e_flags = elf::EF_MIPS_ARCH_64R2 | elf::EF_MIPS_CPIC | elf::EF_MIPS_PIC; + let e_flags = elf::EF_MIPS_CPIC + | elf::EF_MIPS_PIC + | if sess.target.options.cpu.contains("r6") { + elf::EF_MIPS_ARCH_64R6 | elf::EF_MIPS_NAN2008 + } else { + elf::EF_MIPS_ARCH_64R2 + }; file.flags = FileFlags::Elf { e_flags }; } Architecture::Riscv64 if sess.target.options.features.contains("+d") => { diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 00e76800d47..9ecab82dd2e 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -53,14 +53,14 @@ fn push_debuginfo_type_name<'tcx>( ) { // When targeting MSVC, emit C++ style type names for compatibility with // .natvis visualizers (and perhaps other existing native debuggers?) - let cpp_like_names = cpp_like_names(tcx); + let cpp_like_debuginfo = cpp_like_debuginfo(tcx); match *t.kind() { ty::Bool => output.push_str("bool"), ty::Char => output.push_str("char"), ty::Str => output.push_str("str"), ty::Never => { - if cpp_like_names { + if cpp_like_debuginfo { output.push_str("never$"); } else { output.push('!'); @@ -71,7 +71,7 @@ fn push_debuginfo_type_name<'tcx>( ty::Float(float_ty) => output.push_str(float_ty.name_str()), ty::Foreign(def_id) => push_item_name(tcx, def_id, qualified, output), ty::Adt(def, substs) => { - if def.is_enum() && cpp_like_names { + if def.is_enum() && cpp_like_debuginfo { msvc_enum_fallback(tcx, t, def, substs, output, visited); } else { push_item_name(tcx, def.did, qualified, output); @@ -79,7 +79,7 @@ fn push_debuginfo_type_name<'tcx>( } } ty::Tuple(component_types) => { - if cpp_like_names { + if cpp_like_debuginfo { output.push_str("tuple$<"); } else { output.push('('); @@ -87,20 +87,20 @@ fn push_debuginfo_type_name<'tcx>( for component_type in component_types { push_debuginfo_type_name(tcx, component_type.expect_ty(), true, output, visited); - push_arg_separator(cpp_like_names, output); + push_arg_separator(cpp_like_debuginfo, output); } if !component_types.is_empty() { pop_arg_separator(output); } - if cpp_like_names { - push_close_angle_bracket(cpp_like_names, output); + if cpp_like_debuginfo { + push_close_angle_bracket(cpp_like_debuginfo, output); } else { output.push(')'); } } ty::RawPtr(ty::TypeAndMut { ty: inner_type, mutbl }) => { - if cpp_like_names { + if cpp_like_debuginfo { match mutbl { hir::Mutability::Not => output.push_str("ptr_const$<"), hir::Mutability::Mut => output.push_str("ptr_mut$<"), @@ -115,8 +115,8 @@ fn push_debuginfo_type_name<'tcx>( push_debuginfo_type_name(tcx, inner_type, qualified, output, visited); - if cpp_like_names { - push_close_angle_bracket(cpp_like_names, output); + if cpp_like_debuginfo { + push_close_angle_bracket(cpp_like_debuginfo, output); } } ty::Ref(_, inner_type, mutbl) => { @@ -126,7 +126,7 @@ fn push_debuginfo_type_name<'tcx>( // types out to aid debugging in MSVC. let is_slice_or_str = matches!(*inner_type.kind(), ty::Slice(_) | ty::Str); - if !cpp_like_names { + if !cpp_like_debuginfo { output.push('&'); output.push_str(mutbl.prefix_str()); } else if !is_slice_or_str { @@ -138,12 +138,12 @@ fn push_debuginfo_type_name<'tcx>( push_debuginfo_type_name(tcx, inner_type, qualified, output, visited); - if cpp_like_names && !is_slice_or_str { - push_close_angle_bracket(cpp_like_names, output); + if cpp_like_debuginfo && !is_slice_or_str { + push_close_angle_bracket(cpp_like_debuginfo, output); } } ty::Array(inner_type, len) => { - if cpp_like_names { + if cpp_like_debuginfo { output.push_str("array$<"); push_debuginfo_type_name(tcx, inner_type, true, output, visited); match len.val { @@ -162,7 +162,7 @@ fn push_debuginfo_type_name<'tcx>( } } ty::Slice(inner_type) => { - if cpp_like_names { + if cpp_like_debuginfo { output.push_str("slice$<"); } else { output.push('['); @@ -170,8 +170,8 @@ fn push_debuginfo_type_name<'tcx>( push_debuginfo_type_name(tcx, inner_type, true, output, visited); - if cpp_like_names { - push_close_angle_bracket(cpp_like_names, output); + if cpp_like_debuginfo { + push_close_angle_bracket(cpp_like_debuginfo, output); } else { output.push(']'); } @@ -179,7 +179,7 @@ fn push_debuginfo_type_name<'tcx>( ty::Dynamic(ref trait_data, ..) => { let auto_traits: SmallVec<[DefId; 4]> = trait_data.auto_traits().collect(); - let has_enclosing_parens = if cpp_like_names { + let has_enclosing_parens = if cpp_like_debuginfo { output.push_str("dyn$<"); false } else { @@ -216,14 +216,14 @@ fn push_debuginfo_type_name<'tcx>( } for (item_def_id, ty) in projection_bounds { - push_arg_separator(cpp_like_names, output); + push_arg_separator(cpp_like_debuginfo, output); - if cpp_like_names { + if cpp_like_debuginfo { output.push_str("assoc$<"); push_item_name(tcx, item_def_id, false, output); - push_arg_separator(cpp_like_names, output); + push_arg_separator(cpp_like_debuginfo, output); push_debuginfo_type_name(tcx, ty, true, output, visited); - push_close_angle_bracket(cpp_like_names, output); + push_close_angle_bracket(cpp_like_debuginfo, output); } else { push_item_name(tcx, item_def_id, false, output); output.push('='); @@ -231,11 +231,11 @@ fn push_debuginfo_type_name<'tcx>( } } - push_close_angle_bracket(cpp_like_names, output); + push_close_angle_bracket(cpp_like_debuginfo, output); } if auto_traits.len() != 0 { - push_auto_trait_separator(cpp_like_names, output); + push_auto_trait_separator(cpp_like_debuginfo, output); } } @@ -252,14 +252,14 @@ fn push_debuginfo_type_name<'tcx>( for auto_trait in auto_traits { output.push_str(&auto_trait); - push_auto_trait_separator(cpp_like_names, output); + push_auto_trait_separator(cpp_like_debuginfo, output); } pop_auto_trait_separator(output); } - if cpp_like_names { - push_close_angle_bracket(cpp_like_names, output); + if cpp_like_debuginfo { + push_close_angle_bracket(cpp_like_debuginfo, output); } else if has_enclosing_parens { output.push(')'); } @@ -279,7 +279,7 @@ fn push_debuginfo_type_name<'tcx>( // use a dummy string that should make it clear // that something unusual is going on if !visited.insert(t) { - output.push_str(if cpp_like_names { + output.push_str(if cpp_like_debuginfo { "recursive_type$" } else { "<recursive_type>" @@ -290,7 +290,7 @@ fn push_debuginfo_type_name<'tcx>( let sig = tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), t.fn_sig(tcx)); - if cpp_like_names { + if cpp_like_debuginfo { // Format as a C++ function pointer: return_type (*)(params...) if sig.output().is_unit() { output.push_str("void"); @@ -313,7 +313,7 @@ fn push_debuginfo_type_name<'tcx>( if !sig.inputs().is_empty() { for ¶meter_type in sig.inputs() { push_debuginfo_type_name(tcx, parameter_type, true, output, visited); - push_arg_separator(cpp_like_names, output); + push_arg_separator(cpp_like_debuginfo, output); } pop_arg_separator(output); } @@ -328,7 +328,7 @@ fn push_debuginfo_type_name<'tcx>( output.push(')'); - if !cpp_like_names && !sig.output().is_unit() { + if !cpp_like_debuginfo && !sig.output().is_unit() { output.push_str(" -> "); push_debuginfo_type_name(tcx, sig.output(), true, output, visited); } @@ -409,14 +409,14 @@ fn push_debuginfo_type_name<'tcx>( let max = dataful_discriminant_range.end; let max = tag.value.size(&tcx).truncate(max); - let dataful_variant_name = def.variants[*dataful_variant].ident.as_str(); + let dataful_variant_name = def.variants[*dataful_variant].name.as_str(); output.push_str(&format!(", {}, {}, {}", min, max, dataful_variant_name)); } else if let Variants::Single { index: variant_idx } = &layout.variants { // Uninhabited enums can't be constructed and should never need to be visualized so // skip this step for them. if def.variants.len() != 0 { - let variant = def.variants[*variant_idx].ident.as_str(); + let variant = def.variants[*variant_idx].name.as_str(); output.push_str(&format!(", {}", variant)); } @@ -426,9 +426,9 @@ fn push_debuginfo_type_name<'tcx>( const NON_CPP_AUTO_TRAIT_SEPARATOR: &str = " + "; - fn push_auto_trait_separator(cpp_like_names: bool, output: &mut String) { - if cpp_like_names { - push_arg_separator(cpp_like_names, output); + fn push_auto_trait_separator(cpp_like_debuginfo: bool, output: &mut String) { + if cpp_like_debuginfo { + push_arg_separator(cpp_like_debuginfo, output); } else { output.push_str(NON_CPP_AUTO_TRAIT_SEPARATOR); } @@ -457,11 +457,11 @@ pub fn compute_debuginfo_vtable_name<'tcx>( t: Ty<'tcx>, trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>, ) -> String { - let cpp_like_names = cpp_like_names(tcx); + let cpp_like_debuginfo = cpp_like_debuginfo(tcx); let mut vtable_name = String::with_capacity(64); - if cpp_like_names { + if cpp_like_debuginfo { vtable_name.push_str("impl$<"); } else { vtable_name.push('<'); @@ -470,7 +470,7 @@ pub fn compute_debuginfo_vtable_name<'tcx>( let mut visited = FxHashSet::default(); push_debuginfo_type_name(tcx, t, true, &mut vtable_name, &mut visited); - if cpp_like_names { + if cpp_like_debuginfo { vtable_name.push_str(", "); } else { vtable_name.push_str(" as "); @@ -486,9 +486,9 @@ pub fn compute_debuginfo_vtable_name<'tcx>( vtable_name.push_str("_"); } - push_close_angle_bracket(cpp_like_names, &mut vtable_name); + push_close_angle_bracket(cpp_like_debuginfo, &mut vtable_name); - let suffix = if cpp_like_names { "::vtable$" } else { "::{vtable}" }; + let suffix = if cpp_like_debuginfo { "::vtable$" } else { "::{vtable}" }; vtable_name.reserve_exact(suffix.len()); vtable_name.push_str(suffix); @@ -521,7 +521,7 @@ fn push_unqualified_item_name( DefPathData::ClosureExpr if tcx.generator_kind(def_id).is_some() => { // Generators look like closures, but we want to treat them differently // in the debug info. - if cpp_like_names(tcx) { + if cpp_like_debuginfo(tcx) { write!(output, "generator${}", disambiguated_data.disambiguator).unwrap(); } else { write!(output, "{{generator#{}}}", disambiguated_data.disambiguator).unwrap(); @@ -532,7 +532,7 @@ fn push_unqualified_item_name( output.push_str(name.as_str()); } DefPathDataName::Anon { namespace } => { - if cpp_like_names(tcx) { + if cpp_like_debuginfo(tcx) { write!(output, "{}${}", namespace, disambiguated_data.disambiguator).unwrap(); } else { write!(output, "{{{}#{}}}", namespace, disambiguated_data.disambiguator) @@ -560,7 +560,7 @@ fn push_generic_params_internal<'tcx>( debug_assert_eq!(substs, tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), substs)); - let cpp_like_names = cpp_like_names(tcx); + let cpp_like_debuginfo = cpp_like_debuginfo(tcx); output.push('<'); @@ -575,10 +575,10 @@ fn push_generic_params_internal<'tcx>( other => bug!("Unexpected non-erasable generic: {:?}", other), } - push_arg_separator(cpp_like_names, output); + push_arg_separator(cpp_like_debuginfo, output); } pop_arg_separator(output); - push_close_angle_bracket(cpp_like_names, output); + push_close_angle_bracket(cpp_like_debuginfo, output); true } @@ -617,7 +617,7 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: &'tcx ty::Const<'tcx>, output: // avoiding collisions and will make the emitted type names shorter. let hash: u64 = hasher.finish(); - if cpp_like_names(tcx) { + if cpp_like_debuginfo(tcx) { write!(output, "CONST${:x}", hash) } else { write!(output, "{{CONST#{:x}}}", hash) @@ -634,10 +634,10 @@ pub fn push_generic_params<'tcx>(tcx: TyCtxt<'tcx>, substs: SubstsRef<'tcx>, out push_generic_params_internal(tcx, substs, output, &mut visited); } -fn push_close_angle_bracket(cpp_like_names: bool, output: &mut String) { +fn push_close_angle_bracket(cpp_like_debuginfo: bool, output: &mut String) { // MSVC debugger always treats `>>` as a shift, even when parsing templates, // so add a space to avoid confusion. - if cpp_like_names && output.ends_with('>') { + if cpp_like_debuginfo && output.ends_with('>') { output.push(' ') }; @@ -652,11 +652,11 @@ fn pop_close_angle_bracket(output: &mut String) { } } -fn push_arg_separator(cpp_like_names: bool, output: &mut String) { +fn push_arg_separator(cpp_like_debuginfo: bool, output: &mut String) { // Natvis does not always like having spaces between parts of the type name // and this causes issues when we need to write a typename in natvis, for example // as part of a cast like the `HashMap` visualizer does. - if cpp_like_names { + if cpp_like_debuginfo { output.push(','); } else { output.push_str(", "); @@ -673,6 +673,7 @@ fn pop_arg_separator(output: &mut String) { output.pop(); } -fn cpp_like_names(tcx: TyCtxt<'_>) -> bool { +/// Check if we should generate C++ like names and debug information. +pub fn cpp_like_debuginfo(tcx: TyCtxt<'_>) -> bool { tcx.sess.target.is_like_msvc } diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 5a398c2f45a..9dc7930fc51 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -267,14 +267,14 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' match layout.variants { Variants::Single { index } => { // Inside a variant - PathElem::Field(def.variants[index].fields[field].ident.name) + PathElem::Field(def.variants[index].fields[field].name) } Variants::Multiple { .. } => bug!("we handled variants above"), } } // other ADTs - ty::Adt(def, _) => PathElem::Field(def.non_enum_variant().fields[field].ident.name), + ty::Adt(def, _) => PathElem::Field(def.non_enum_variant().fields[field].name), // arrays/slices ty::Array(..) | ty::Slice(..) => PathElem::ArrayElem(field), @@ -726,7 +726,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> new_op: &OpTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx> { let name = match old_op.layout.ty.kind() { - ty::Adt(adt, _) => PathElem::Variant(adt.variants[variant_id].ident.name), + ty::Adt(adt, _) => PathElem::Variant(adt.variants[variant_id].name), // Generators also have variants ty::Generator(..) => PathElem::GeneratorState(variant_id), _ => bug!("Unexpected type with variant: {:?}", old_op.layout.ty), 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 dd749c03934..de4824eb667 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -810,7 +810,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { param_env, Binder::dummy(TraitPredicate { trait_ref, - constness: ty::BoundConstness::ConstIfConst, + constness: ty::BoundConstness::NotConst, polarity: ty::ImplPolarity::Positive, }), ); @@ -829,6 +829,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { return; } Ok(Some(ImplSource::UserDefined(data))) => { + if let hir::Constness::NotConst = tcx.impl_constness(data.impl_def_id) { + self.check_op(ops::FnCallNonConst(None)); + return; + } let callee_name = tcx.item_name(callee); if let Some(&did) = tcx .associated_item_def_ids(data.impl_def_id) diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index 3da35178957..9c09a7f5f82 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -583,3 +583,22 @@ fn stable_hash_reduce<HCX, I, C, F>( } } } + +#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)] +pub enum NodeIdHashingMode { + Ignore, + HashDefPath, +} + +/// Controls what data we do or not not hash. +/// Whenever a `HashStable` implementation caches its +/// result, it needs to include `HashingControls` as part +/// of the key, to ensure that is does not produce an incorrect +/// result (for example, using a `Fingerprint` produced while +/// hashing `Span`s when a `Fingeprint` without `Span`s is +/// being requested) +#[derive(Clone, Hash, Eq, PartialEq, Debug)] +pub struct HashingControls { + pub hash_spans: bool, + pub node_id_hashing_mode: NodeIdHashingMode, +} diff --git a/compiler/rustc_data_structures/src/thin_vec/tests.rs b/compiler/rustc_data_structures/src/thin_vec/tests.rs index 5abfd939373..0221b9912bb 100644 --- a/compiler/rustc_data_structures/src/thin_vec/tests.rs +++ b/compiler/rustc_data_structures/src/thin_vec/tests.rs @@ -10,8 +10,8 @@ impl<T> ThinVec<T> { fn test_from_iterator() { assert_eq!(std::iter::empty().collect::<ThinVec<String>>().into_vec(), Vec::<String>::new()); assert_eq!(std::iter::once(42).collect::<ThinVec<_>>().into_vec(), vec![42]); - assert_eq!(vec![1, 2].into_iter().collect::<ThinVec<_>>().into_vec(), vec![1, 2]); - assert_eq!(vec![1, 2, 3].into_iter().collect::<ThinVec<_>>().into_vec(), vec![1, 2, 3]); + assert_eq!([1, 2].into_iter().collect::<ThinVec<_>>().into_vec(), vec![1, 2]); + assert_eq!([1, 2, 3].into_iter().collect::<ThinVec<_>>().into_vec(), vec![1, 2, 3]); } #[test] diff --git a/compiler/rustc_data_structures/src/vec_map/tests.rs b/compiler/rustc_data_structures/src/vec_map/tests.rs index 9083de85982..458b60077dc 100644 --- a/compiler/rustc_data_structures/src/vec_map/tests.rs +++ b/compiler/rustc_data_structures/src/vec_map/tests.rs @@ -14,7 +14,7 @@ fn test_from_iterator() { ); assert_eq!(std::iter::once((42, true)).collect::<VecMap<_, _>>().into_vec(), vec![(42, true)]); assert_eq!( - vec![(1, true), (2, false)].into_iter().collect::<VecMap<_, _>>().into_vec(), + [(1, true), (2, false)].into_iter().collect::<VecMap<_, _>>().into_vec(), vec![(1, true), (2, false)] ); } @@ -41,7 +41,7 @@ fn test_insert() { #[test] fn test_get() { - let v = vec![(1, true), (2, false)].into_iter().collect::<VecMap<_, _>>(); + let v = [(1, true), (2, false)].into_iter().collect::<VecMap<_, _>>(); assert_eq!(v.get(&1), Some(&true)); assert_eq!(v.get(&2), Some(&false)); assert_eq!(v.get(&3), None); diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index dde978cd8c6..c2af2b2a86d 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -455,7 +455,7 @@ impl DiagnosticSpan { let backtrace_step = backtrace.next().map(|bt| { let call_site = Self::from_span_full(bt.call_site, false, None, None, backtrace, je); let def_site_span = - Self::from_span_full(bt.def_site, false, None, None, vec![].into_iter(), je); + Self::from_span_full(bt.def_site, false, None, None, [].into_iter(), je); Box::new(DiagnosticSpanMacroExpansion { span: call_site, macro_decl_name: bt.kind.descr(), diff --git a/compiler/rustc_errors/src/snippet.rs b/compiler/rustc_errors/src/snippet.rs index 64353461e90..e4cc44c41dd 100644 --- a/compiler/rustc_errors/src/snippet.rs +++ b/compiler/rustc_errors/src/snippet.rs @@ -69,9 +69,6 @@ pub enum AnnotationType { /// Annotation under a single line of code Singleline, - /// Annotation enclosing the first and last character of a multiline span - Multiline(MultilineAnnotation), - // The Multiline type above is replaced with the following three in order // to reuse the current label drawing code. // diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index db0dea48708..e0bdeb30dc8 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -238,7 +238,7 @@ macro_rules! configure { } impl<'a> StripUnconfigured<'a> { - pub fn configure<T: AstLike>(&mut self, mut node: T) -> Option<T> { + pub fn configure<T: AstLike>(&self, mut node: T) -> Option<T> { self.process_cfg_attrs(&mut node); if self.in_cfg(node.attrs()) { self.try_configure_tokens(&mut node); @@ -248,7 +248,7 @@ impl<'a> StripUnconfigured<'a> { } } - fn try_configure_tokens<T: AstLike>(&mut self, node: &mut T) { + fn try_configure_tokens<T: AstLike>(&self, node: &mut T) { if self.config_tokens { if let Some(Some(tokens)) = node.tokens_mut() { let attr_annotated_tokens = tokens.create_token_stream(); @@ -257,10 +257,7 @@ impl<'a> StripUnconfigured<'a> { } } - fn configure_krate_attrs( - &mut self, - mut attrs: Vec<ast::Attribute>, - ) -> Option<Vec<ast::Attribute>> { + fn configure_krate_attrs(&self, mut attrs: Vec<ast::Attribute>) -> Option<Vec<ast::Attribute>> { attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr)); if self.in_cfg(&attrs) { Some(attrs) } else { None } } @@ -269,7 +266,7 @@ impl<'a> StripUnconfigured<'a> { /// This is only used during the invocation of `derive` proc-macros, /// which require that we cfg-expand their entire input. /// Normal cfg-expansion operates on parsed AST nodes via the `configure` method - fn configure_tokens(&mut self, stream: &AttrAnnotatedTokenStream) -> AttrAnnotatedTokenStream { + fn configure_tokens(&self, stream: &AttrAnnotatedTokenStream) -> AttrAnnotatedTokenStream { fn can_skip(stream: &AttrAnnotatedTokenStream) -> bool { stream.0.iter().all(|(tree, _spacing)| match tree { AttrAnnotatedTokenTree::Attributes(_) => false, @@ -325,7 +322,7 @@ impl<'a> StripUnconfigured<'a> { /// Gives compiler warnings if any `cfg_attr` does not contain any /// attributes and is in the original source code. Gives compiler errors if /// the syntax of any `cfg_attr` is incorrect. - fn process_cfg_attrs<T: AstLike>(&mut self, node: &mut T) { + fn process_cfg_attrs<T: AstLike>(&self, node: &mut T) { node.visit_attrs(|attrs| { attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr)); }); @@ -338,7 +335,7 @@ impl<'a> StripUnconfigured<'a> { /// Gives a compiler warning when the `cfg_attr` contains no attributes and /// is in the original source file. Gives a compiler error if the syntax of /// the attribute is incorrect. - fn process_cfg_attr(&mut self, attr: Attribute) -> Vec<Attribute> { + fn process_cfg_attr(&self, attr: Attribute) -> Vec<Attribute> { if !attr.has_name(sym::cfg_attr) { return vec![attr]; } @@ -461,7 +458,7 @@ impl<'a> StripUnconfigured<'a> { } } - pub fn configure_expr(&mut self, expr: &mut P<ast::Expr>) { + pub fn configure_expr(&self, expr: &mut P<ast::Expr>) { for attr in expr.attrs.iter() { self.maybe_emit_expr_attr_err(attr); } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 7f49f80a843..07ce901fb41 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1,6 +1,5 @@ use crate::base::*; use crate::config::StripUnconfigured; -use crate::configure; use crate::hygiene::SyntaxContext; use crate::mbe::macro_rules::annotate_err_with_kind; use crate::module::{mod_dir_path, parse_external_mod, DirOwnership, ParsedExternalMod}; @@ -12,13 +11,12 @@ use rustc_ast::ptr::P; use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{self, AssocCtxt, Visitor}; -use rustc_ast::{AstLike, Block, Inline, ItemKind, MacArgs, MacCall}; -use rustc_ast::{MacCallStmt, MacStmtStyle, MetaItemKind, ModKind, NestedMetaItem}; -use rustc_ast::{NodeId, PatKind, Path, StmtKind}; +use rustc_ast::{AssocItemKind, AstLike, AstLikeWrapper, AttrStyle, ExprKind, ForeignItemKind}; +use rustc_ast::{Inline, ItemKind, MacArgs, MacStmtStyle, MetaItemKind, ModKind, NestedMetaItem}; +use rustc_ast::{NodeId, PatKind, StmtKind, TyKind}; use rustc_ast_pretty::pprust; use rustc_attr::is_builtin_attr; use rustc_data_structures::map_in_place::MapInPlace; -use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, PResult}; use rustc_feature::Features; @@ -34,7 +32,7 @@ use rustc_span::symbol::{sym, Ident}; use rustc_span::{FileName, LocalExpnId, Span}; use smallvec::SmallVec; -use std::ops::DerefMut; +use std::ops::Deref; use std::path::PathBuf; use std::rc::Rc; use std::{iter, mem}; @@ -109,6 +107,10 @@ macro_rules! ast_fragments { } })* + fn make_ast<T: InvocationCollectorNode>(self) -> T::OutputTy { + T::fragment_to_output(self) + } + pub fn mut_visit_with<F: MutVisitor>(&mut self, vis: &mut F) { match self { AstFragment::OptExpr(opt_expr) => { @@ -178,10 +180,10 @@ ast_fragments! { Arms(SmallVec<[ast::Arm; 1]>) { "match arm"; many fn flat_map_arm; fn visit_arm(); fn make_arms; } - Fields(SmallVec<[ast::ExprField; 1]>) { + ExprFields(SmallVec<[ast::ExprField; 1]>) { "field expression"; many fn flat_map_expr_field; fn visit_expr_field(); fn make_expr_fields; } - FieldPats(SmallVec<[ast::PatField; 1]>) { + PatFields(SmallVec<[ast::PatField; 1]>) { "field pattern"; many fn flat_map_pat_field; fn visit_pat_field(); @@ -196,7 +198,7 @@ ast_fragments! { Params(SmallVec<[ast::Param; 1]>) { "function parameter"; many fn flat_map_param; fn visit_param(); fn make_params; } - StructFields(SmallVec<[ast::FieldDef; 1]>) { + FieldDefs(SmallVec<[ast::FieldDef; 1]>) { "field"; many fn flat_map_field_def; fn visit_field_def(); @@ -231,11 +233,11 @@ impl AstFragmentKind { | AstFragmentKind::ForeignItems | AstFragmentKind::Crate => SupportsMacroExpansion::Yes { supports_inner_attrs: true }, AstFragmentKind::Arms - | AstFragmentKind::Fields - | AstFragmentKind::FieldPats + | AstFragmentKind::ExprFields + | AstFragmentKind::PatFields | AstFragmentKind::GenericParams | AstFragmentKind::Params - | AstFragmentKind::StructFields + | AstFragmentKind::FieldDefs | AstFragmentKind::Variants => SupportsMacroExpansion::No, } } @@ -249,11 +251,11 @@ impl AstFragmentKind { AstFragmentKind::Arms => { AstFragment::Arms(items.map(Annotatable::expect_arm).collect()) } - AstFragmentKind::Fields => { - AstFragment::Fields(items.map(Annotatable::expect_expr_field).collect()) + AstFragmentKind::ExprFields => { + AstFragment::ExprFields(items.map(Annotatable::expect_expr_field).collect()) } - AstFragmentKind::FieldPats => { - AstFragment::FieldPats(items.map(Annotatable::expect_pat_field).collect()) + AstFragmentKind::PatFields => { + AstFragment::PatFields(items.map(Annotatable::expect_pat_field).collect()) } AstFragmentKind::GenericParams => { AstFragment::GenericParams(items.map(Annotatable::expect_generic_param).collect()) @@ -261,8 +263,8 @@ impl AstFragmentKind { AstFragmentKind::Params => { AstFragment::Params(items.map(Annotatable::expect_param).collect()) } - AstFragmentKind::StructFields => { - AstFragment::StructFields(items.map(Annotatable::expect_field_def).collect()) + AstFragmentKind::FieldDefs => { + AstFragment::FieldDefs(items.map(Annotatable::expect_field_def).collect()) } AstFragmentKind::Variants => { AstFragment::Variants(items.map(Annotatable::expect_variant).collect()) @@ -315,10 +317,10 @@ pub enum InvocationKind { pos: usize, item: Annotatable, // Required for resolving derive helper attributes. - derives: Vec<Path>, + derives: Vec<ast::Path>, }, Derive { - path: Path, + path: ast::Path, item: Annotatable, }, } @@ -676,7 +678,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { krate, ), Annotatable::Item(item_inner) - if matches!(attr.style, ast::AttrStyle::Inner) + if matches!(attr.style, AttrStyle::Inner) && matches!( item_inner.kind, ItemKind::Mod( @@ -744,7 +746,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { if let SyntaxExtensionKind::Derive(..) = ext { self.gate_proc_macro_input(&item); } - let meta = ast::MetaItem { kind: ast::MetaItemKind::Word, span, path }; + let meta = ast::MetaItem { kind: MetaItemKind::Word, span, path }; let items = match expander.expand(self.cx, span, &meta, item) { ExpandResult::Ready(items) => items, ExpandResult::Retry(item) => { @@ -806,7 +808,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { impl<'ast, 'a> Visitor<'ast> for GateProcMacroInput<'a> { fn visit_item(&mut self, item: &'ast ast::Item) { match &item.kind { - ast::ItemKind::Mod(_, mod_kind) + ItemKind::Mod(_, mod_kind) if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _)) => { feature_err( @@ -834,7 +836,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { &mut self, toks: TokenStream, kind: AstFragmentKind, - path: &Path, + path: &ast::Path, span: Span, ) -> AstFragment { let mut parser = self.cx.new_parser_from_tts(toks); @@ -915,18 +917,18 @@ pub fn parse_ast_fragment<'a>( )?), AstFragmentKind::Crate => AstFragment::Crate(this.parse_crate_mod()?), AstFragmentKind::Arms - | AstFragmentKind::Fields - | AstFragmentKind::FieldPats + | AstFragmentKind::ExprFields + | AstFragmentKind::PatFields | AstFragmentKind::GenericParams | AstFragmentKind::Params - | AstFragmentKind::StructFields + | AstFragmentKind::FieldDefs | AstFragmentKind::Variants => panic!("unexpected AST fragment kind"), }) } pub fn ensure_complete_parse<'a>( this: &mut Parser<'a>, - macro_path: &Path, + macro_path: &ast::Path, kind_name: &str, span: Span, ) { @@ -961,6 +963,568 @@ pub fn ensure_complete_parse<'a>( } } +/// Wraps a call to `noop_visit_*` / `noop_flat_map_*` +/// for an AST node that supports attributes +/// (see the `Annotatable` enum) +/// This method assigns a `NodeId`, and sets that `NodeId` +/// as our current 'lint node id'. If a macro call is found +/// inside this AST node, we will use this AST node's `NodeId` +/// to emit lints associated with that macro (allowing +/// `#[allow]` / `#[deny]` to be applied close to +/// the macro invocation). +/// +/// Do *not* call this for a macro AST node +/// (e.g. `ExprKind::MacCall`) - we cannot emit lints +/// at these AST nodes, since they are removed and +/// replaced with the result of macro expansion. +/// +/// All other `NodeId`s are assigned by `visit_id`. +/// * `self` is the 'self' parameter for the current method, +/// * `id` is a mutable reference to the `NodeId` field +/// of the current AST node. +/// * `closure` is a closure that executes the +/// `noop_visit_*` / `noop_flat_map_*` method +/// for the current AST node. +macro_rules! assign_id { + ($self:ident, $id:expr, $closure:expr) => {{ + let old_id = $self.cx.current_expansion.lint_node_id; + if $self.monotonic { + debug_assert_eq!(*$id, ast::DUMMY_NODE_ID); + let new_id = $self.cx.resolver.next_node_id(); + *$id = new_id; + $self.cx.current_expansion.lint_node_id = new_id; + } + let ret = ($closure)(); + $self.cx.current_expansion.lint_node_id = old_id; + ret + }}; +} + +enum AddSemicolon { + Yes, + No, +} + +/// A trait implemented for all `AstFragment` nodes and providing all pieces +/// of functionality used by `InvocationCollector`. +trait InvocationCollectorNode: AstLike { + type OutputTy = SmallVec<[Self; 1]>; + type AttrsTy: Deref<Target = [ast::Attribute]> = Vec<ast::Attribute>; + const KIND: AstFragmentKind; + fn to_annotatable(self) -> Annotatable; + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy; + fn id(&mut self) -> &mut NodeId; + fn noop_flat_map<V: MutVisitor>(self, _visitor: &mut V) -> Self::OutputTy { + unreachable!() + } + fn noop_visit<V: MutVisitor>(&mut self, _visitor: &mut V) { + unreachable!() + } + fn is_mac_call(&self) -> bool { + false + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) { + unreachable!() + } + fn pre_flat_map_node_collect_attr(_cfg: &StripUnconfigured<'_>, _attr: &ast::Attribute) {} + fn post_flat_map_node_collect_bang(_output: &mut Self::OutputTy, _add_semicolon: AddSemicolon) { + } + fn wrap_flat_map_node_noop_flat_map( + node: Self, + collector: &mut InvocationCollector<'_, '_>, + noop_flat_map: impl FnOnce(Self, &mut InvocationCollector<'_, '_>) -> Self::OutputTy, + ) -> Result<Self::OutputTy, Self> { + Ok(noop_flat_map(node, collector)) + } +} + +impl InvocationCollectorNode for P<ast::Item> { + const KIND: AstFragmentKind = AstFragmentKind::Items; + fn to_annotatable(self) -> Annotatable { + Annotatable::Item(self) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_items() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_item(self, visitor) + } + fn is_mac_call(&self) -> bool { + matches!(self.kind, ItemKind::MacCall(..)) + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) { + let node = self.into_inner(); + match node.kind { + ItemKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No), + _ => unreachable!(), + } + } + fn wrap_flat_map_node_noop_flat_map( + mut node: Self, + collector: &mut InvocationCollector<'_, '_>, + noop_flat_map: impl FnOnce(Self, &mut InvocationCollector<'_, '_>) -> Self::OutputTy, + ) -> Result<Self::OutputTy, Self> { + if !matches!(node.kind, ItemKind::Mod(..)) { + return Ok(noop_flat_map(node, collector)); + } + + // Work around borrow checker not seeing through `P`'s deref. + let (ident, span, mut attrs) = (node.ident, node.span, mem::take(&mut node.attrs)); + let ItemKind::Mod(_, mod_kind) = &mut node.kind else { + unreachable!() + }; + + let ecx = &mut collector.cx; + let (file_path, dir_path, dir_ownership) = match mod_kind { + ModKind::Loaded(_, inline, _) => { + // Inline `mod foo { ... }`, but we still need to push directories. + let (dir_path, dir_ownership) = mod_dir_path( + &ecx.sess, + ident, + &attrs, + &ecx.current_expansion.module, + ecx.current_expansion.dir_ownership, + *inline, + ); + node.attrs = attrs; + (None, dir_path, dir_ownership) + } + ModKind::Unloaded => { + // We have an outline `mod foo;` so we need to parse the file. + let old_attrs_len = attrs.len(); + let ParsedExternalMod { mut items, inner_span, file_path, dir_path, dir_ownership } = + parse_external_mod( + &ecx.sess, + ident, + span, + &ecx.current_expansion.module, + ecx.current_expansion.dir_ownership, + &mut attrs, + ); + + if let Some(extern_mod_loaded) = ecx.extern_mod_loaded { + (attrs, items) = extern_mod_loaded(ident, attrs, items, inner_span); + } + + *mod_kind = ModKind::Loaded(items, Inline::No, inner_span); + node.attrs = attrs; + if node.attrs.len() > old_attrs_len { + // If we loaded an out-of-line module and added some inner attributes, + // then we need to re-configure it and re-collect attributes for + // resolution and expansion. + return Err(node); + } + (Some(file_path), dir_path, dir_ownership) + } + }; + + // Set the module info before we flat map. + let mut module = ecx.current_expansion.module.with_dir_path(dir_path); + module.mod_path.push(ident); + if let Some(file_path) = file_path { + module.file_path_stack.push(file_path); + } + + let orig_module = mem::replace(&mut ecx.current_expansion.module, Rc::new(module)); + let orig_dir_ownership = + mem::replace(&mut ecx.current_expansion.dir_ownership, dir_ownership); + + let res = Ok(noop_flat_map(node, collector)); + + collector.cx.current_expansion.dir_ownership = orig_dir_ownership; + collector.cx.current_expansion.module = orig_module; + res + } +} + +struct TraitItemTag; +impl InvocationCollectorNode for AstLikeWrapper<P<ast::AssocItem>, TraitItemTag> { + type OutputTy = SmallVec<[P<ast::AssocItem>; 1]>; + const KIND: AstFragmentKind = AstFragmentKind::TraitItems; + fn to_annotatable(self) -> Annotatable { + Annotatable::TraitItem(self.wrapped) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_trait_items() + } + fn id(&mut self) -> &mut NodeId { + &mut self.wrapped.id + } + fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_assoc_item(self.wrapped, visitor) + } + fn is_mac_call(&self) -> bool { + matches!(self.wrapped.kind, AssocItemKind::MacCall(..)) + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) { + let item = self.wrapped.into_inner(); + match item.kind { + AssocItemKind::MacCall(mac) => (mac, item.attrs, AddSemicolon::No), + _ => unreachable!(), + } + } +} + +struct ImplItemTag; +impl InvocationCollectorNode for AstLikeWrapper<P<ast::AssocItem>, ImplItemTag> { + type OutputTy = SmallVec<[P<ast::AssocItem>; 1]>; + const KIND: AstFragmentKind = AstFragmentKind::ImplItems; + fn to_annotatable(self) -> Annotatable { + Annotatable::ImplItem(self.wrapped) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_impl_items() + } + fn id(&mut self) -> &mut NodeId { + &mut self.wrapped.id + } + fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_assoc_item(self.wrapped, visitor) + } + fn is_mac_call(&self) -> bool { + matches!(self.wrapped.kind, AssocItemKind::MacCall(..)) + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) { + let item = self.wrapped.into_inner(); + match item.kind { + AssocItemKind::MacCall(mac) => (mac, item.attrs, AddSemicolon::No), + _ => unreachable!(), + } + } +} + +impl InvocationCollectorNode for P<ast::ForeignItem> { + const KIND: AstFragmentKind = AstFragmentKind::ForeignItems; + fn to_annotatable(self) -> Annotatable { + Annotatable::ForeignItem(self) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_foreign_items() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_foreign_item(self, visitor) + } + fn is_mac_call(&self) -> bool { + matches!(self.kind, ForeignItemKind::MacCall(..)) + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) { + let node = self.into_inner(); + match node.kind { + ForeignItemKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No), + _ => unreachable!(), + } + } +} + +impl InvocationCollectorNode for ast::Variant { + const KIND: AstFragmentKind = AstFragmentKind::Variants; + fn to_annotatable(self) -> Annotatable { + Annotatable::Variant(self) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_variants() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_variant(self, visitor) + } +} + +impl InvocationCollectorNode for ast::FieldDef { + const KIND: AstFragmentKind = AstFragmentKind::FieldDefs; + fn to_annotatable(self) -> Annotatable { + Annotatable::FieldDef(self) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_field_defs() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_field_def(self, visitor) + } +} + +impl InvocationCollectorNode for ast::PatField { + const KIND: AstFragmentKind = AstFragmentKind::PatFields; + fn to_annotatable(self) -> Annotatable { + Annotatable::PatField(self) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_pat_fields() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_pat_field(self, visitor) + } +} + +impl InvocationCollectorNode for ast::ExprField { + const KIND: AstFragmentKind = AstFragmentKind::ExprFields; + fn to_annotatable(self) -> Annotatable { + Annotatable::ExprField(self) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_expr_fields() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_expr_field(self, visitor) + } +} + +impl InvocationCollectorNode for ast::Param { + const KIND: AstFragmentKind = AstFragmentKind::Params; + fn to_annotatable(self) -> Annotatable { + Annotatable::Param(self) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_params() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_param(self, visitor) + } +} + +impl InvocationCollectorNode for ast::GenericParam { + const KIND: AstFragmentKind = AstFragmentKind::GenericParams; + fn to_annotatable(self) -> Annotatable { + Annotatable::GenericParam(self) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_generic_params() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_generic_param(self, visitor) + } +} + +impl InvocationCollectorNode for ast::Arm { + const KIND: AstFragmentKind = AstFragmentKind::Arms; + fn to_annotatable(self) -> Annotatable { + Annotatable::Arm(self) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_arms() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_arm(self, visitor) + } +} + +impl InvocationCollectorNode for ast::Stmt { + type AttrsTy = ast::AttrVec; + const KIND: AstFragmentKind = AstFragmentKind::Stmts; + fn to_annotatable(self) -> Annotatable { + Annotatable::Stmt(P(self)) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_stmts() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_stmt(self, visitor) + } + fn is_mac_call(&self) -> bool { + match &self.kind { + StmtKind::MacCall(..) => true, + StmtKind::Item(item) => matches!(item.kind, ItemKind::MacCall(..)), + StmtKind::Semi(expr) => matches!(expr.kind, ExprKind::MacCall(..)), + StmtKind::Expr(..) => unreachable!(), + StmtKind::Local(..) | StmtKind::Empty => false, + } + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) { + // We pull macro invocations (both attributes and fn-like macro calls) out of their + // `StmtKind`s and treat them as statement macro invocations, not as items or expressions. + let (add_semicolon, mac, attrs) = match self.kind { + StmtKind::MacCall(mac) => { + let ast::MacCallStmt { mac, style, attrs, .. } = mac.into_inner(); + (style == MacStmtStyle::Semicolon, mac, attrs) + } + StmtKind::Item(item) => match item.into_inner() { + ast::Item { kind: ItemKind::MacCall(mac), attrs, .. } => { + (mac.args.need_semicolon(), mac, attrs.into()) + } + _ => unreachable!(), + }, + StmtKind::Semi(expr) => match expr.into_inner() { + ast::Expr { kind: ExprKind::MacCall(mac), attrs, .. } => { + (mac.args.need_semicolon(), mac, attrs) + } + _ => unreachable!(), + }, + _ => unreachable!(), + }; + (mac, attrs, if add_semicolon { AddSemicolon::Yes } else { AddSemicolon::No }) + } + fn post_flat_map_node_collect_bang(stmts: &mut Self::OutputTy, add_semicolon: AddSemicolon) { + // If this is a macro invocation with a semicolon, then apply that + // semicolon to the final statement produced by expansion. + if matches!(add_semicolon, AddSemicolon::Yes) { + if let Some(stmt) = stmts.pop() { + stmts.push(stmt.add_trailing_semicolon()); + } + } + } +} + +impl InvocationCollectorNode for ast::Crate { + type OutputTy = ast::Crate; + const KIND: AstFragmentKind = AstFragmentKind::Crate; + fn to_annotatable(self) -> Annotatable { + Annotatable::Crate(self) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_crate() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_visit<V: MutVisitor>(&mut self, visitor: &mut V) { + noop_visit_crate(self, visitor) + } +} + +impl InvocationCollectorNode for P<ast::Ty> { + type OutputTy = P<ast::Ty>; + const KIND: AstFragmentKind = AstFragmentKind::Ty; + fn to_annotatable(self) -> Annotatable { + unreachable!() + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_ty() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_visit<V: MutVisitor>(&mut self, visitor: &mut V) { + noop_visit_ty(self, visitor) + } + fn is_mac_call(&self) -> bool { + matches!(self.kind, ast::TyKind::MacCall(..)) + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) { + let node = self.into_inner(); + match node.kind { + TyKind::MacCall(mac) => (mac, Vec::new(), AddSemicolon::No), + _ => unreachable!(), + } + } +} + +impl InvocationCollectorNode for P<ast::Pat> { + type OutputTy = P<ast::Pat>; + const KIND: AstFragmentKind = AstFragmentKind::Pat; + fn to_annotatable(self) -> Annotatable { + unreachable!() + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_pat() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_visit<V: MutVisitor>(&mut self, visitor: &mut V) { + noop_visit_pat(self, visitor) + } + fn is_mac_call(&self) -> bool { + matches!(self.kind, PatKind::MacCall(..)) + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) { + let node = self.into_inner(); + match node.kind { + PatKind::MacCall(mac) => (mac, Vec::new(), AddSemicolon::No), + _ => unreachable!(), + } + } +} + +impl InvocationCollectorNode for P<ast::Expr> { + type OutputTy = P<ast::Expr>; + type AttrsTy = ast::AttrVec; + const KIND: AstFragmentKind = AstFragmentKind::Expr; + fn to_annotatable(self) -> Annotatable { + Annotatable::Expr(self) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_expr() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_visit<V: MutVisitor>(&mut self, visitor: &mut V) { + noop_visit_expr(self, visitor) + } + fn is_mac_call(&self) -> bool { + matches!(self.kind, ExprKind::MacCall(..)) + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) { + let node = self.into_inner(); + match node.kind { + ExprKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No), + _ => unreachable!(), + } + } +} + +struct OptExprTag; +impl InvocationCollectorNode for AstLikeWrapper<P<ast::Expr>, OptExprTag> { + type OutputTy = Option<P<ast::Expr>>; + type AttrsTy = ast::AttrVec; + const KIND: AstFragmentKind = AstFragmentKind::OptExpr; + fn to_annotatable(self) -> Annotatable { + Annotatable::Expr(self.wrapped) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_opt_expr() + } + fn id(&mut self) -> &mut NodeId { + &mut self.wrapped.id + } + fn noop_flat_map<V: MutVisitor>(mut self, visitor: &mut V) -> Self::OutputTy { + noop_visit_expr(&mut self.wrapped, visitor); + Some(self.wrapped) + } + fn is_mac_call(&self) -> bool { + matches!(self.wrapped.kind, ast::ExprKind::MacCall(..)) + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) { + let node = self.wrapped.into_inner(); + match node.kind { + ExprKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No), + _ => unreachable!(), + } + } + fn pre_flat_map_node_collect_attr(cfg: &StripUnconfigured<'_>, attr: &ast::Attribute) { + cfg.maybe_emit_expr_attr_err(&attr); + } +} + struct InvocationCollector<'a, 'b> { cx: &'a mut ExtCtxt<'b>, cfg: StripUnconfigured<'a>, @@ -996,7 +1560,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { fn collect_attr( &mut self, - (attr, pos, derives): (ast::Attribute, usize, Vec<Path>), + (attr, pos, derives): (ast::Attribute, usize, Vec<ast::Path>), item: Annotatable, kind: AstFragmentKind, ) -> AstFragment { @@ -1007,9 +1571,9 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { /// its position and derives following it. We have to collect the derives in order to resolve /// legacy derive helpers (helpers written before derives that introduce them). fn take_first_attr( - &mut self, + &self, item: &mut impl AstLike, - ) -> Option<(ast::Attribute, usize, Vec<Path>)> { + ) -> Option<(ast::Attribute, usize, Vec<ast::Path>)> { let mut attr = None; item.visit_attrs(|attrs| { @@ -1039,45 +1603,13 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { attr } - fn take_stmt_bang( - &mut self, - stmt: ast::Stmt, - ) -> Result<(bool, MacCall, Vec<ast::Attribute>), ast::Stmt> { - match stmt.kind { - StmtKind::MacCall(mac) => { - let MacCallStmt { mac, style, attrs, .. } = mac.into_inner(); - Ok((style == MacStmtStyle::Semicolon, mac, attrs.into())) - } - StmtKind::Item(item) if matches!(item.kind, ItemKind::MacCall(..)) => { - match item.into_inner() { - ast::Item { kind: ItemKind::MacCall(mac), attrs, .. } => { - Ok((mac.args.need_semicolon(), mac, attrs)) - } - _ => unreachable!(), - } - } - StmtKind::Semi(expr) if matches!(expr.kind, ast::ExprKind::MacCall(..)) => { - match expr.into_inner() { - ast::Expr { kind: ast::ExprKind::MacCall(mac), attrs, .. } => { - Ok((mac.args.need_semicolon(), mac, attrs.into())) - } - _ => unreachable!(), - } - } - StmtKind::Local(..) | StmtKind::Empty | StmtKind::Item(..) | StmtKind::Semi(..) => { - Err(stmt) - } - StmtKind::Expr(..) => unreachable!(), - } - } - fn configure<T: AstLike>(&mut self, node: T) -> Option<T> { self.cfg.configure(node) } // Detect use of feature-gated or invalid attributes on macro invocations // since they will not be detected after macro expansion. - fn check_attributes(&self, attrs: &[ast::Attribute], call: &MacCall) { + fn check_attributes(&self, attrs: &[ast::Attribute], call: &ast::MacCall) { let features = self.cx.ecfg.features.unwrap(); let mut attrs = attrs.iter().peekable(); let mut span: Option<Span> = None; @@ -1120,510 +1652,165 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { } } } -} -/// Wraps a call to `noop_visit_*` / `noop_flat_map_*` -/// for an AST node that supports attributes -/// (see the `Annotatable` enum) -/// This method assigns a `NodeId`, and sets that `NodeId` -/// as our current 'lint node id'. If a macro call is found -/// inside this AST node, we will use this AST node's `NodeId` -/// to emit lints associated with that macro (allowing -/// `#[allow]` / `#[deny]` to be applied close to -/// the macro invocation). -/// -/// Do *not* call this for a macro AST node -/// (e.g. `ExprKind::MacCall`) - we cannot emit lints -/// at these AST nodes, since they are removed and -/// replaced with the result of macro expansion. -/// -/// All other `NodeId`s are assigned by `visit_id`. -/// * `self` is the 'self' parameter for the current method, -/// * `id` is a mutable reference to the `NodeId` field -/// of the current AST node. -/// * `closure` is a closure that executes the -/// `noop_visit_*` / `noop_flat_map_*` method -/// for the current AST node. -macro_rules! assign_id { - ($self:ident, $id:expr, $closure:expr) => {{ - let old_id = $self.cx.current_expansion.lint_node_id; - if $self.monotonic { - debug_assert_eq!(*$id, ast::DUMMY_NODE_ID); - let new_id = $self.cx.resolver.next_node_id(); - *$id = new_id; - $self.cx.current_expansion.lint_node_id = new_id; + fn flat_map_node<Node: InvocationCollectorNode<OutputTy: Default>>( + &mut self, + node: Node, + ) -> Node::OutputTy { + let mut node = configure!(self, node); + + if let Some(attr) = self.take_first_attr(&mut node) { + Node::pre_flat_map_node_collect_attr(&self.cfg, &attr.0); + self.collect_attr(attr, node.to_annotatable(), Node::KIND).make_ast::<Node>() + } else if node.is_mac_call() { + let (mac, attrs, add_semicolon) = node.take_mac_call(); + self.check_attributes(&attrs, &mac); + let mut res = self.collect_bang(mac, Node::KIND).make_ast::<Node>(); + Node::post_flat_map_node_collect_bang(&mut res, add_semicolon); + res + } else { + match Node::wrap_flat_map_node_noop_flat_map(node, self, |mut node, this| { + assign_id!(this, node.id(), || node.noop_flat_map(this)) + }) { + Ok(output) => output, + Err(node) => self.flat_map_node(node), + } } - let ret = ($closure)(); - $self.cx.current_expansion.lint_node_id = old_id; - ret - }}; + } + + fn visit_node<Node: InvocationCollectorNode<OutputTy = Node> + DummyAstNode>( + &mut self, + node: &mut Node, + ) { + if let Some(attr) = self.take_first_attr(node) { + visit_clobber(node, |node| { + self.collect_attr(attr, node.to_annotatable(), Node::KIND).make_ast::<Node>() + }) + } else if node.is_mac_call() { + visit_clobber(node, |node| { + // Do not clobber unless it's actually a macro (uncommon case). + let (mac, attrs, _) = node.take_mac_call(); + self.check_attributes(&attrs, &mac); + self.collect_bang(mac, Node::KIND).make_ast::<Node>() + }) + } else { + assign_id!(self, node.id(), || node.noop_visit(self)) + } + } } impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { - fn visit_crate(&mut self, krate: &mut ast::Crate) { - visit_clobber(krate, |krate| { - let span = krate.span; - let mut krate = match self.configure(krate) { - Some(krate) => krate, - None => { - return ast::Crate { - attrs: Vec::new(), - items: Vec::new(), - span, - id: self.cx.resolver.next_node_id(), - is_placeholder: false, - }; - } - }; - - if let Some(attr) = self.take_first_attr(&mut krate) { - return self - .collect_attr(attr, Annotatable::Crate(krate), AstFragmentKind::Crate) - .make_crate(); - } - - assign_id!(self, &mut krate.id, || noop_visit_crate(&mut krate, self)); - krate - }) + fn flat_map_item(&mut self, node: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> { + self.flat_map_node(node) } - fn visit_expr(&mut self, expr: &mut P<ast::Expr>) { - self.cfg.configure_expr(expr); - visit_clobber(expr.deref_mut(), |mut expr| { - if let Some(attr) = self.take_first_attr(&mut expr) { - // Collect the invoc regardless of whether or not attributes are permitted here - // expansion will eat the attribute so it won't error later. - self.cfg.maybe_emit_expr_attr_err(&attr.0); - - // AstFragmentKind::Expr requires the macro to emit an expression. - return self - .collect_attr(attr, Annotatable::Expr(P(expr)), AstFragmentKind::Expr) - .make_expr() - .into_inner(); - } - - if let ast::ExprKind::MacCall(mac) = expr.kind { - self.check_attributes(&expr.attrs, &mac); - self.collect_bang(mac, AstFragmentKind::Expr).make_expr().into_inner() - } else { - assign_id!(self, &mut expr.id, || { - ensure_sufficient_stack(|| noop_visit_expr(&mut expr, self)); - }); - expr - } - }); + fn flat_map_trait_item(&mut self, node: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> { + self.flat_map_node(AstLikeWrapper::new(node, TraitItemTag)) } - fn flat_map_arm(&mut self, arm: ast::Arm) -> SmallVec<[ast::Arm; 1]> { - let mut arm = configure!(self, arm); - - if let Some(attr) = self.take_first_attr(&mut arm) { - return self - .collect_attr(attr, Annotatable::Arm(arm), AstFragmentKind::Arms) - .make_arms(); - } - - assign_id!(self, &mut arm.id, || noop_flat_map_arm(arm, self)) + fn flat_map_impl_item(&mut self, node: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> { + self.flat_map_node(AstLikeWrapper::new(node, ImplItemTag)) } - fn flat_map_expr_field(&mut self, field: ast::ExprField) -> SmallVec<[ast::ExprField; 1]> { - let mut field = configure!(self, field); - - if let Some(attr) = self.take_first_attr(&mut field) { - return self - .collect_attr(attr, Annotatable::ExprField(field), AstFragmentKind::Fields) - .make_expr_fields(); - } - - assign_id!(self, &mut field.id, || noop_flat_map_expr_field(field, self)) + fn flat_map_foreign_item( + &mut self, + node: P<ast::ForeignItem>, + ) -> SmallVec<[P<ast::ForeignItem>; 1]> { + self.flat_map_node(node) } - fn flat_map_pat_field(&mut self, fp: ast::PatField) -> SmallVec<[ast::PatField; 1]> { - let mut fp = configure!(self, fp); - - if let Some(attr) = self.take_first_attr(&mut fp) { - return self - .collect_attr(attr, Annotatable::PatField(fp), AstFragmentKind::FieldPats) - .make_pat_fields(); - } - - assign_id!(self, &mut fp.id, || noop_flat_map_pat_field(fp, self)) + fn flat_map_variant(&mut self, node: ast::Variant) -> SmallVec<[ast::Variant; 1]> { + self.flat_map_node(node) } - fn flat_map_param(&mut self, p: ast::Param) -> SmallVec<[ast::Param; 1]> { - let mut p = configure!(self, p); - - if let Some(attr) = self.take_first_attr(&mut p) { - return self - .collect_attr(attr, Annotatable::Param(p), AstFragmentKind::Params) - .make_params(); - } - - assign_id!(self, &mut p.id, || noop_flat_map_param(p, self)) + fn flat_map_field_def(&mut self, node: ast::FieldDef) -> SmallVec<[ast::FieldDef; 1]> { + self.flat_map_node(node) } - fn flat_map_field_def(&mut self, sf: ast::FieldDef) -> SmallVec<[ast::FieldDef; 1]> { - let mut sf = configure!(self, sf); - - if let Some(attr) = self.take_first_attr(&mut sf) { - return self - .collect_attr(attr, Annotatable::FieldDef(sf), AstFragmentKind::StructFields) - .make_field_defs(); - } - - assign_id!(self, &mut sf.id, || noop_flat_map_field_def(sf, self)) + fn flat_map_pat_field(&mut self, node: ast::PatField) -> SmallVec<[ast::PatField; 1]> { + self.flat_map_node(node) } - fn flat_map_variant(&mut self, variant: ast::Variant) -> SmallVec<[ast::Variant; 1]> { - let mut variant = configure!(self, variant); - - if let Some(attr) = self.take_first_attr(&mut variant) { - return self - .collect_attr(attr, Annotatable::Variant(variant), AstFragmentKind::Variants) - .make_variants(); - } - - assign_id!(self, &mut variant.id, || noop_flat_map_variant(variant, self)) + fn flat_map_expr_field(&mut self, node: ast::ExprField) -> SmallVec<[ast::ExprField; 1]> { + self.flat_map_node(node) } - fn filter_map_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> { - let expr = configure!(self, expr); - expr.filter_map(|mut expr| { - if let Some(attr) = self.take_first_attr(&mut expr) { - self.cfg.maybe_emit_expr_attr_err(&attr.0); - - return self - .collect_attr(attr, Annotatable::Expr(P(expr)), AstFragmentKind::OptExpr) - .make_opt_expr() - .map(|expr| expr.into_inner()); - } - - if let ast::ExprKind::MacCall(mac) = expr.kind { - self.check_attributes(&expr.attrs, &mac); - self.collect_bang(mac, AstFragmentKind::OptExpr) - .make_opt_expr() - .map(|expr| expr.into_inner()) - } else { - assign_id!(self, &mut expr.id, || { - Some({ - noop_visit_expr(&mut expr, self); - expr - }) - }) - } - }) + fn flat_map_param(&mut self, node: ast::Param) -> SmallVec<[ast::Param; 1]> { + self.flat_map_node(node) } - fn visit_pat(&mut self, pat: &mut P<ast::Pat>) { - match pat.kind { - PatKind::MacCall(_) => {} - _ => return noop_visit_pat(pat, self), - } - - visit_clobber(pat, |mut pat| match mem::replace(&mut pat.kind, PatKind::Wild) { - PatKind::MacCall(mac) => self.collect_bang(mac, AstFragmentKind::Pat).make_pat(), - _ => unreachable!(), - }); + fn flat_map_generic_param( + &mut self, + node: ast::GenericParam, + ) -> SmallVec<[ast::GenericParam; 1]> { + self.flat_map_node(node) } - fn flat_map_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> { - let mut stmt = configure!(self, stmt); + fn flat_map_arm(&mut self, node: ast::Arm) -> SmallVec<[ast::Arm; 1]> { + self.flat_map_node(node) + } - // We pull macro invocations (both attributes and fn-like macro calls) out of their - // `StmtKind`s and treat them as statement macro invocations, not as items or expressions. + fn flat_map_stmt(&mut self, node: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> { // FIXME: invocations in semicolon-less expressions positions are expanded as expressions, // changing that requires some compatibility measures. - let mut stmt = if !stmt.is_expr() { - if let Some(attr) = self.take_first_attr(&mut stmt) { - return self - .collect_attr(attr, Annotatable::Stmt(P(stmt)), AstFragmentKind::Stmts) - .make_stmts(); - } - - match self.take_stmt_bang(stmt) { - Ok((add_semicolon, mac, attrs)) => { - self.check_attributes(&attrs, &mac); - let mut stmts = self.collect_bang(mac, AstFragmentKind::Stmts).make_stmts(); - - // If this is a macro invocation with a semicolon, then apply that - // semicolon to the final statement produced by expansion. - if add_semicolon { - if let Some(stmt) = stmts.pop() { - stmts.push(stmt.add_trailing_semicolon()); - } - } - - return stmts; + if node.is_expr() { + // The only way that we can end up with a `MacCall` expression statement, + // (as opposed to a `StmtKind::MacCall`) is if we have a macro as the + // traiing expression in a block (e.g. `fn foo() { my_macro!() }`). + // Record this information, so that we can report a more specific + // `SEMICOLON_IN_EXPRESSIONS_FROM_MACROS` lint if needed. + // See #78991 for an investigation of treating macros in this position + // as statements, rather than expressions, during parsing. + let mut node = configure!(self, node); + return match &node.kind { + StmtKind::Expr(expr) + if matches!(**expr, ast::Expr { kind: ExprKind::MacCall(..), .. }) => + { + self.cx.current_expansion.is_trailing_mac = true; + // Don't use `assign_id` for this statement - it may get removed + // entirely due to a `#[cfg]` on the contained expression + let res = noop_flat_map_stmt(node, self); + self.cx.current_expansion.is_trailing_mac = false; + res } - Err(stmt) => stmt, - } - } else { - stmt - }; - - // The only way that we can end up with a `MacCall` expression statement, - // (as opposed to a `StmtKind::MacCall`) is if we have a macro as the - // traiing expression in a block (e.g. `fn foo() { my_macro!() }`). - // Record this information, so that we can report a more specific - // `SEMICOLON_IN_EXPRESSIONS_FROM_MACROS` lint if needed. - // See #78991 for an investigation of treating macros in this position - // as statements, rather than expressions, during parsing. - let res = match &stmt.kind { - StmtKind::Expr(expr) - if matches!(**expr, ast::Expr { kind: ast::ExprKind::MacCall(..), .. }) => - { - self.cx.current_expansion.is_trailing_mac = true; - // Don't use `assign_id` for this statement - it may get removed - // entirely due to a `#[cfg]` on the contained expression - noop_flat_map_stmt(stmt, self) - } - _ => assign_id!(self, &mut stmt.id, || noop_flat_map_stmt(stmt, self)), - }; - self.cx.current_expansion.is_trailing_mac = false; - res - } - - fn visit_block(&mut self, block: &mut P<Block>) { - let orig_dir_ownership = mem::replace( - &mut self.cx.current_expansion.dir_ownership, - DirOwnership::UnownedViaBlock, - ); - noop_visit_block(block, self); - self.cx.current_expansion.dir_ownership = orig_dir_ownership; - } - - fn flat_map_item(&mut self, item: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> { - let mut item = configure!(self, item); - - if let Some(attr) = self.take_first_attr(&mut item) { - return self - .collect_attr(attr, Annotatable::Item(item), AstFragmentKind::Items) - .make_items(); + _ => assign_id!(self, &mut node.id, || noop_flat_map_stmt(node, self)), + }; } - let mut attrs = mem::take(&mut item.attrs); // We do this to please borrowck. - let ident = item.ident; - let span = item.span; - - match item.kind { - ast::ItemKind::MacCall(ref mac) => { - self.check_attributes(&attrs, &mac); - item.attrs = attrs; - item.and_then(|item| match item.kind { - ItemKind::MacCall(mac) => { - self.collect_bang(mac, AstFragmentKind::Items).make_items() - } - _ => unreachable!(), - }) - } - ast::ItemKind::Mod(_, ref mut mod_kind) if ident != Ident::empty() => { - let (file_path, dir_path, dir_ownership) = match mod_kind { - ModKind::Loaded(_, inline, _) => { - // Inline `mod foo { ... }`, but we still need to push directories. - let (dir_path, dir_ownership) = mod_dir_path( - &self.cx.sess, - ident, - &attrs, - &self.cx.current_expansion.module, - self.cx.current_expansion.dir_ownership, - *inline, - ); - item.attrs = attrs; - (None, dir_path, dir_ownership) - } - ModKind::Unloaded => { - // We have an outline `mod foo;` so we need to parse the file. - let old_attrs_len = attrs.len(); - let ParsedExternalMod { - mut items, - inner_span, - file_path, - dir_path, - dir_ownership, - } = parse_external_mod( - &self.cx.sess, - ident, - span, - &self.cx.current_expansion.module, - self.cx.current_expansion.dir_ownership, - &mut attrs, - ); - - if let Some(extern_mod_loaded) = self.cx.extern_mod_loaded { - (attrs, items) = extern_mod_loaded(ident, attrs, items, inner_span); - } - - *mod_kind = ModKind::Loaded(items, Inline::No, inner_span); - item.attrs = attrs; - if item.attrs.len() > old_attrs_len { - // If we loaded an out-of-line module and added some inner attributes, - // then we need to re-configure it and re-collect attributes for - // resolution and expansion. - item = configure!(self, item); - - if let Some(attr) = self.take_first_attr(&mut item) { - return self - .collect_attr( - attr, - Annotatable::Item(item), - AstFragmentKind::Items, - ) - .make_items(); - } - } - (Some(file_path), dir_path, dir_ownership) - } - }; - - // Set the module info before we flat map. - let mut module = self.cx.current_expansion.module.with_dir_path(dir_path); - module.mod_path.push(ident); - if let Some(file_path) = file_path { - module.file_path_stack.push(file_path); - } - - let orig_module = - mem::replace(&mut self.cx.current_expansion.module, Rc::new(module)); - let orig_dir_ownership = - mem::replace(&mut self.cx.current_expansion.dir_ownership, dir_ownership); - - let result = assign_id!(self, &mut item.id, || noop_flat_map_item(item, self)); - - // Restore the module info. - self.cx.current_expansion.dir_ownership = orig_dir_ownership; - self.cx.current_expansion.module = orig_module; - - result - } - _ => { - item.attrs = attrs; - // The crate root is special - don't assign an ID to it. - if !(matches!(item.kind, ast::ItemKind::Mod(..)) && ident == Ident::empty()) { - assign_id!(self, &mut item.id, || noop_flat_map_item(item, self)) - } else { - noop_flat_map_item(item, self) - } - } - } + self.flat_map_node(node) } - fn flat_map_trait_item(&mut self, item: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> { - let mut item = configure!(self, item); - - if let Some(attr) = self.take_first_attr(&mut item) { - return self - .collect_attr(attr, Annotatable::TraitItem(item), AstFragmentKind::TraitItems) - .make_trait_items(); - } - - match item.kind { - ast::AssocItemKind::MacCall(ref mac) => { - self.check_attributes(&item.attrs, &mac); - item.and_then(|item| match item.kind { - ast::AssocItemKind::MacCall(mac) => { - self.collect_bang(mac, AstFragmentKind::TraitItems).make_trait_items() - } - _ => unreachable!(), - }) - } - _ => { - assign_id!(self, &mut item.id, || noop_flat_map_assoc_item(item, self)) - } - } + fn visit_crate(&mut self, node: &mut ast::Crate) { + self.visit_node(node) } - fn flat_map_impl_item(&mut self, item: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> { - let mut item = configure!(self, item); - - if let Some(attr) = self.take_first_attr(&mut item) { - return self - .collect_attr(attr, Annotatable::ImplItem(item), AstFragmentKind::ImplItems) - .make_impl_items(); - } - - match item.kind { - ast::AssocItemKind::MacCall(ref mac) => { - self.check_attributes(&item.attrs, &mac); - item.and_then(|item| match item.kind { - ast::AssocItemKind::MacCall(mac) => { - self.collect_bang(mac, AstFragmentKind::ImplItems).make_impl_items() - } - _ => unreachable!(), - }) - } - _ => { - assign_id!(self, &mut item.id, || noop_flat_map_assoc_item(item, self)) - } - } + fn visit_ty(&mut self, node: &mut P<ast::Ty>) { + self.visit_node(node) } - fn visit_ty(&mut self, ty: &mut P<ast::Ty>) { - match ty.kind { - ast::TyKind::MacCall(_) => {} - _ => return noop_visit_ty(ty, self), - }; - - visit_clobber(ty, |mut ty| match mem::replace(&mut ty.kind, ast::TyKind::Err) { - ast::TyKind::MacCall(mac) => self.collect_bang(mac, AstFragmentKind::Ty).make_ty(), - _ => unreachable!(), - }); + fn visit_pat(&mut self, node: &mut P<ast::Pat>) { + self.visit_node(node) } - fn flat_map_foreign_item( - &mut self, - foreign_item: P<ast::ForeignItem>, - ) -> SmallVec<[P<ast::ForeignItem>; 1]> { - let mut foreign_item = configure!(self, foreign_item); - - if let Some(attr) = self.take_first_attr(&mut foreign_item) { - return self - .collect_attr( - attr, - Annotatable::ForeignItem(foreign_item), - AstFragmentKind::ForeignItems, - ) - .make_foreign_items(); - } - - match foreign_item.kind { - ast::ForeignItemKind::MacCall(ref mac) => { - self.check_attributes(&foreign_item.attrs, &mac); - foreign_item.and_then(|item| match item.kind { - ast::ForeignItemKind::MacCall(mac) => { - self.collect_bang(mac, AstFragmentKind::ForeignItems).make_foreign_items() - } - _ => unreachable!(), - }) - } - _ => { - assign_id!(self, &mut foreign_item.id, || noop_flat_map_foreign_item( - foreign_item, - self - )) - } - } + fn visit_expr(&mut self, node: &mut P<ast::Expr>) { + self.cfg.configure_expr(node); + self.visit_node(node) } - fn flat_map_generic_param( - &mut self, - param: ast::GenericParam, - ) -> SmallVec<[ast::GenericParam; 1]> { - let mut param = configure!(self, param); - - if let Some(attr) = self.take_first_attr(&mut param) { - return self - .collect_attr( - attr, - Annotatable::GenericParam(param), - AstFragmentKind::GenericParams, - ) - .make_generic_params(); - } + fn filter_map_expr(&mut self, node: P<ast::Expr>) -> Option<P<ast::Expr>> { + self.flat_map_node(AstLikeWrapper::new(node, OptExprTag)) + } - assign_id!(self, &mut param.id, || noop_flat_map_generic_param(param, self)) + fn visit_block(&mut self, node: &mut P<ast::Block>) { + let orig_dir_ownership = mem::replace( + &mut self.cx.current_expansion.dir_ownership, + DirOwnership::UnownedViaBlock, + ); + noop_visit_block(node, self); + self.cx.current_expansion.dir_ownership = orig_dir_ownership; } - fn visit_id(&mut self, id: &mut ast::NodeId) { + fn visit_id(&mut self, id: &mut NodeId) { // We may have already assigned a `NodeId` // by calling `assign_id` if self.monotonic && *id == ast::DUMMY_NODE_ID { diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index 47a64b457d0..5599c1df6d9 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -1,3 +1,5 @@ +#![feature(associated_type_bounds)] +#![feature(associated_type_defaults)] #![feature(crate_visibility_modifier)] #![feature(decl_macro)] #![cfg_attr(bootstrap, feature(destructuring_assignment))] diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index 825af9a7b2b..af593e92634 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -123,7 +123,7 @@ pub fn placeholder( span, is_placeholder: true, }]), - AstFragmentKind::Fields => AstFragment::Fields(smallvec![ast::ExprField { + AstFragmentKind::ExprFields => AstFragment::ExprFields(smallvec![ast::ExprField { attrs: Default::default(), expr: expr_placeholder(), id, @@ -132,7 +132,7 @@ pub fn placeholder( span, is_placeholder: true, }]), - AstFragmentKind::FieldPats => AstFragment::FieldPats(smallvec![ast::PatField { + AstFragmentKind::PatFields => AstFragment::PatFields(smallvec![ast::PatField { attrs: Default::default(), id, ident, @@ -159,7 +159,7 @@ pub fn placeholder( ty: ty(), is_placeholder: true, }]), - AstFragmentKind::StructFields => AstFragment::StructFields(smallvec![ast::FieldDef { + AstFragmentKind::FieldDefs => AstFragment::FieldDefs(smallvec![ast::FieldDef { attrs: Default::default(), id, ident: None, diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 56564656556..efbe0b65715 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -158,7 +158,7 @@ impl FromInternal<(TreeAndSpacing, &'_ mut Vec<Self>, &mut Rustc<'_, '_>)> for ch in data.as_str().chars() { escaped.extend(ch.escape_debug()); } - let stream = vec![ + let stream = [ Ident(sym::doc, false), Eq, TokenKind::lit(token::Str, Symbol::intern(&escaped), None), @@ -221,7 +221,7 @@ impl ToInternal<TokenStream> for TokenTree<Group, Punct, Ident, Literal> { let integer = TokenKind::lit(token::Integer, symbol, suffix); let a = tokenstream::TokenTree::token(minus, span); let b = tokenstream::TokenTree::token(integer, span); - return vec![a, b].into_iter().collect(); + return [a, b].into_iter().collect(); } TokenTree::Literal(self::Literal { lit: token::Lit { kind: token::Float, symbol, suffix }, @@ -232,7 +232,7 @@ impl ToInternal<TokenStream> for TokenTree<Group, Punct, Ident, Literal> { let float = TokenKind::lit(token::Float, symbol, suffix); let a = tokenstream::TokenTree::token(minus, span); let b = tokenstream::TokenTree::token(float, span); - return vec![a, b].into_iter().collect(); + return [a, b].into_iter().collect(); } TokenTree::Literal(self::Literal { lit, span }) => { return tokenstream::TokenTree::token(Literal(lit), span).into(); diff --git a/compiler/rustc_graphviz/src/tests.rs b/compiler/rustc_graphviz/src/tests.rs index a297bac86c4..154bae4cb05 100644 --- a/compiler/rustc_graphviz/src/tests.rs +++ b/compiler/rustc_graphviz/src/tests.rs @@ -56,7 +56,7 @@ impl NodeLabels<&'static str> { match self { UnlabelledNodes(len) => vec![None; len], AllNodesLabelled(lbls) => lbls.into_iter().map(Some).collect(), - SomeNodesLabelled(lbls) => lbls.into_iter().collect(), + SomeNodesLabelled(lbls) => lbls, } } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index d59756239d9..76f4df6ec2d 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -205,7 +205,6 @@ impl Path<'_> { #[derive(Debug, HashStable_Generic)] pub struct PathSegment<'hir> { /// The identifier portion of this path segment. - #[stable_hasher(project(name))] pub ident: Ident, // `id` and `res` are optional. We currently only use these in save-analysis, // any path segments without these will not have save-analysis info and @@ -294,10 +293,6 @@ impl GenericArg<'_> { } } - pub fn is_const(&self) -> bool { - matches!(self, GenericArg::Const(_)) - } - pub fn is_synthetic(&self) -> bool { matches!(self, GenericArg::Lifetime(lifetime) if lifetime.name.ident() == Ident::empty()) } @@ -319,6 +314,13 @@ impl GenericArg<'_> { GenericArg::Infer(_) => ast::ParamKindOrd::Infer, } } + + pub fn is_ty_or_const(&self) -> bool { + match self { + GenericArg::Lifetime(_) => false, + GenericArg::Type(_) | GenericArg::Const(_) | GenericArg::Infer(_) => true, + } + } } #[derive(Debug, HashStable_Generic)] @@ -850,7 +852,6 @@ pub struct PatField<'hir> { #[stable_hasher(ignore)] pub hir_id: HirId, /// The identifier for the field. - #[stable_hasher(project(name))] pub ident: Ident, /// The pattern the field is destructured to. pub pat: &'hir Pat<'hir>, @@ -2127,7 +2128,6 @@ pub const FN_OUTPUT_NAME: Symbol = sym::Output; #[derive(Debug, HashStable_Generic)] pub struct TypeBinding<'hir> { pub hir_id: HirId, - #[stable_hasher(project(name))] pub ident: Ident, pub gen_args: &'hir GenericArgs<'hir>, pub kind: TypeBindingKind<'hir>, @@ -2515,7 +2515,6 @@ pub struct EnumDef<'hir> { #[derive(Debug, HashStable_Generic)] pub struct Variant<'hir> { /// Name of the variant. - #[stable_hasher(project(name))] pub ident: Ident, /// Id of the variant (not the constructor, see `VariantData::ctor_hir_id()`). pub id: HirId, @@ -2605,7 +2604,6 @@ impl VisibilityKind<'_> { #[derive(Debug, HashStable_Generic)] pub struct FieldDef<'hir> { pub span: Span, - #[stable_hasher(project(name))] pub ident: Ident, pub vis: Visibility<'hir>, pub hir_id: HirId, @@ -2864,7 +2862,6 @@ impl ItemKind<'_> { #[derive(Encodable, Debug, HashStable_Generic)] pub struct TraitItemRef { pub id: TraitItemId, - #[stable_hasher(project(name))] pub ident: Ident, pub kind: AssocItemKind, pub span: Span, @@ -2880,7 +2877,6 @@ pub struct TraitItemRef { #[derive(Debug, HashStable_Generic)] pub struct ImplItemRef { pub id: ImplItemId, - #[stable_hasher(project(name))] pub ident: Ident, pub kind: AssocItemKind, pub span: Span, @@ -2919,7 +2915,6 @@ impl ForeignItemId { #[derive(Debug, HashStable_Generic)] pub struct ForeignItemRef { pub id: ForeignItemId, - #[stable_hasher(project(name))] pub ident: Ident, pub span: Span, } diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index a03c561861e..def0c1d0687 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -151,20 +151,12 @@ impl<CTX> HashStable<CTX> for LangItem { /// Extracts the first `lang = "$name"` out of a list of attributes. /// The attributes `#[panic_handler]` and `#[alloc_error_handler]` /// are also extracted out when found. -/// -/// About the `check_name` argument: passing in a `Session` would be simpler, -/// because then we could call `Session::check_name` directly. But we want to -/// avoid the need for `rustc_hir` to depend on `rustc_session`, so we -/// use a closure instead. -pub fn extract<'a, F>(check_name: F, attrs: &'a [ast::Attribute]) -> Option<(Symbol, Span)> -where - F: Fn(&'a ast::Attribute, Symbol) -> bool, -{ +pub fn extract(attrs: &[ast::Attribute]) -> Option<(Symbol, Span)> { attrs.iter().find_map(|attr| { Some(match attr { - _ if check_name(attr, sym::lang) => (attr.value_str()?, attr.span), - _ if check_name(attr, sym::panic_handler) => (sym::panic_impl, attr.span), - _ if check_name(attr, sym::alloc_error_handler) => (sym::oom, attr.span), + _ if attr.has_name(sym::lang) => (attr.value_str()?, attr.span), + _ if attr.has_name(sym::panic_handler) => (sym::panic_impl, attr.span), + _ if attr.has_name(sym::alloc_error_handler) => (sym::oom, attr.span), _ => return None, }) }) diff --git a/compiler/rustc_hir/src/weak_lang_items.rs b/compiler/rustc_hir/src/weak_lang_items.rs index 58c3065240c..78748209d1a 100644 --- a/compiler/rustc_hir/src/weak_lang_items.rs +++ b/compiler/rustc_hir/src/weak_lang_items.rs @@ -18,13 +18,9 @@ pub static WEAK_ITEMS_REFS: SyncLazy<StableMap<Symbol, LangItem>> = SyncLazy::ne map }); -/// The `check_name` argument avoids the need for `rustc_hir` to depend on -/// `rustc_session`. -pub fn link_name<'a, F>(check_name: F, attrs: &'a [ast::Attribute]) -> Option<Symbol> -where - F: Fn(&'a ast::Attribute, Symbol) -> bool +pub fn link_name(attrs: &[ast::Attribute]) -> Option<Symbol> { - lang_items::extract(check_name, attrs).and_then(|(name, _)| { + lang_items::extract(attrs).and_then(|(name, _)| { $(if name == sym::$name { Some(sym::$sym) } else)* { diff --git a/compiler/rustc_incremental/src/persist/fs/tests.rs b/compiler/rustc_incremental/src/persist/fs/tests.rs index 652ef6bcdce..184796948b6 100644 --- a/compiler/rustc_incremental/src/persist/fs/tests.rs +++ b/compiler/rustc_incremental/src/persist/fs/tests.rs @@ -13,7 +13,7 @@ fn test_all_except_most_recent() { .keys() .cloned() .collect::<FxHashSet<PathBuf>>(), - vec![PathBuf::from("1"), PathBuf::from("2"), PathBuf::from("3"), PathBuf::from("4"),] + [PathBuf::from("1"), PathBuf::from("2"), PathBuf::from("3"), PathBuf::from("4"),] .into_iter() .collect::<FxHashSet<PathBuf>>() ); @@ -40,7 +40,7 @@ fn test_find_source_directory_in_iter() { // Find newest assert_eq!( find_source_directory_in_iter( - vec![ + [ PathBuf::from("crate-dir/s-3234-0000-svh"), PathBuf::from("crate-dir/s-2234-0000-svh"), PathBuf::from("crate-dir/s-1234-0000-svh") @@ -54,7 +54,7 @@ fn test_find_source_directory_in_iter() { // Filter out "-working" assert_eq!( find_source_directory_in_iter( - vec![ + [ PathBuf::from("crate-dir/s-3234-0000-working"), PathBuf::from("crate-dir/s-2234-0000-svh"), PathBuf::from("crate-dir/s-1234-0000-svh") @@ -66,12 +66,12 @@ fn test_find_source_directory_in_iter() { ); // Handle empty - assert_eq!(find_source_directory_in_iter(vec![].into_iter(), &already_visited), None); + assert_eq!(find_source_directory_in_iter([].into_iter(), &already_visited), None); // Handle only working assert_eq!( find_source_directory_in_iter( - vec![ + [ PathBuf::from("crate-dir/s-3234-0000-working"), PathBuf::from("crate-dir/s-2234-0000-working"), PathBuf::from("crate-dir/s-1234-0000-working") diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index f0c73d0c2f3..6d39dadc7ba 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -771,7 +771,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.suggest_boxing_for_return_impl_trait( err, ret_sp, - vec![then, else_sp].into_iter(), + [then, else_sp].into_iter(), ); } } @@ -807,11 +807,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ); let sugg = arm_spans .flat_map(|sp| { - vec![ - (sp.shrink_to_lo(), "Box::new(".to_string()), - (sp.shrink_to_hi(), ")".to_string()), - ] - .into_iter() + [(sp.shrink_to_lo(), "Box::new(".to_string()), (sp.shrink_to_hi(), ")".to_string())] + .into_iter() }) .collect::<Vec<_>>(); err.multipart_suggestion( @@ -1924,7 +1921,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .fields .iter() .filter(|field| field.vis.is_accessible_from(field.did, self.tcx)) - .map(|field| (field.ident.name, field.ty(self.tcx, expected_substs))) + .map(|field| (field.name, field.ty(self.tcx, expected_substs))) .find(|(_, ty)| same_type_modulo_infer(ty, exp_found.found)) { if let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() { diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index 9cf6cde2591..c1cca834f2b 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -12,7 +12,7 @@ use rustc_middle::ty::print::Print; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; use rustc_middle::ty::{self, Const, DefIdTree, InferConst, Ty, TyCtxt, TypeFoldable, TypeFolder}; use rustc_span::symbol::kw; -use rustc_span::Span; +use rustc_span::{sym, Span}; use std::borrow::Cow; struct FindHirNodeVisitor<'a, 'tcx> { @@ -540,8 +540,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // error[E0284]: type annotations needed // --> file.rs:2:5 // | - // 2 | vec![Ok(2)].into_iter().collect()?; - // | ^^^^^^^ cannot infer type + // 2 | [Ok(2)].into_iter().collect()?; + // | ^^^^^^^ cannot infer type // | // = note: cannot resolve `<_ as std::ops::Try>::Ok == _` if span.contains(*call_span) { *call_span } else { span } @@ -1003,9 +1003,9 @@ impl<'tcx> TypeFolder<'tcx> for ResolvedTypeParamEraser<'tcx> { | ty::Opaque(..) | ty::Projection(_) | ty::Never => t.super_fold_with(self), - ty::Array(ty, c) => self - .tcx() - .mk_ty(ty::Array(self.fold_ty(ty), self.replace_infers(c, 0, Symbol::intern("N")))), + ty::Array(ty, c) => { + self.tcx().mk_ty(ty::Array(self.fold_ty(ty), self.replace_infers(c, 0, sym::N))) + } // We don't want to hide type params that haven't been resolved yet. // This would be the type that will be written out with the type param // name in the output. diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 04e04e297cd..d3664e53447 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -10,7 +10,6 @@ pub(crate) use self::undo_log::{InferCtxtUndoLogs, Snapshot, UndoLog}; use crate::traits::{self, ObligationCause, PredicateObligations, TraitEngine}; -use hir::def_id::CRATE_DEF_ID; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; use rustc_data_structures::undo_log::Rollback; @@ -291,7 +290,12 @@ pub struct InferCtxt<'a, 'tcx> { /// The `DefId` of the item in whose context we are performing inference or typeck. /// It is used to check whether an opaque type use is a defining use. - pub defining_use_anchor: LocalDefId, + /// + /// If it is `None`, we can't resolve opaque types here and need to bubble up + /// the obligation. This frequently happens for + /// short lived InferCtxt within queries. The opaque type obligations are forwarded + /// to the outside until the end up in an `InferCtxt` for typeck or borrowck. + pub defining_use_anchor: Option<LocalDefId>, /// During type-checking/inference of a body, `in_progress_typeck_results` /// contains a reference to the typeck results being built up, which are @@ -547,7 +551,7 @@ impl<'tcx> fmt::Display for FixupError<'tcx> { pub struct InferCtxtBuilder<'tcx> { tcx: TyCtxt<'tcx>, fresh_typeck_results: Option<RefCell<ty::TypeckResults<'tcx>>>, - defining_use_anchor: LocalDefId, + defining_use_anchor: Option<LocalDefId>, } pub trait TyCtxtInferExt<'tcx> { @@ -556,11 +560,7 @@ pub trait TyCtxtInferExt<'tcx> { impl<'tcx> TyCtxtInferExt<'tcx> for TyCtxt<'tcx> { fn infer_ctxt(self) -> InferCtxtBuilder<'tcx> { - InferCtxtBuilder { - tcx: self, - defining_use_anchor: CRATE_DEF_ID, - fresh_typeck_results: None, - } + InferCtxtBuilder { tcx: self, defining_use_anchor: None, fresh_typeck_results: None } } } @@ -580,7 +580,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> { /// (via `with_fresh_in_progress_typeck_results`) and for the inference context used /// in mir borrowck. pub fn with_opaque_type_inference(mut self, defining_use_anchor: LocalDefId) -> Self { - self.defining_use_anchor = defining_use_anchor; + self.defining_use_anchor = Some(defining_use_anchor); self } diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index c2ef0b41e27..04b1a42e5be 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -328,6 +328,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }, }); } + + fn opaque_type_origin(&self, def_id: LocalDefId) -> Option<hir::OpaqueTyOrigin> { + let tcx = self.tcx; + let opaque_hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + let parent_def_id = self.defining_use_anchor?; + let item_kind = &tcx.hir().expect_item(def_id).kind; + let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item_kind else { + span_bug!( + tcx.def_span(def_id), + "weird opaque type: {:#?}", + item_kind + ) + }; + let in_definition_scope = match *origin { + // Async `impl Trait` + hir::OpaqueTyOrigin::AsyncFn(parent) => parent == parent_def_id, + // Anonymous `impl Trait` + hir::OpaqueTyOrigin::FnReturn(parent) => parent == parent_def_id, + // Named `type Foo = impl Bar;` + hir::OpaqueTyOrigin::TyAlias => { + may_define_opaque_type(tcx, parent_def_id, opaque_hir_id) + } + }; + in_definition_scope.then_some(*origin) + } } // Visitor that requires that (almost) all regions in the type visited outlive @@ -459,31 +484,10 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { // } // ``` if let Some(def_id) = def_id.as_local() { - let opaque_hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let parent_def_id = self.infcx.defining_use_anchor; - let item_kind = &tcx.hir().expect_item(def_id).kind; - let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item_kind else { - span_bug!( - self.value_span, - "weird opaque type: {:#?}, {:#?}", - ty.kind(), - item_kind - ) - }; - let in_definition_scope = match *origin { - // Async `impl Trait` - hir::OpaqueTyOrigin::AsyncFn(parent) => parent == parent_def_id, - // Anonymous `impl Trait` - hir::OpaqueTyOrigin::FnReturn(parent) => parent == parent_def_id, - // Named `type Foo = impl Bar;` - hir::OpaqueTyOrigin::TyAlias => { - may_define_opaque_type(tcx, parent_def_id, opaque_hir_id) - } - }; - if in_definition_scope { + if let Some(origin) = self.infcx.opaque_type_origin(def_id) { let opaque_type_key = OpaqueTypeKey { def_id: def_id.to_def_id(), substs }; - return self.fold_opaque_ty(ty, opaque_type_key, *origin); + return self.fold_opaque_ty(ty, opaque_type_key, origin); } debug!( @@ -551,6 +555,22 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { let predicate = predicate.subst(tcx, substs); debug!(?predicate); + let predicate = predicate.fold_with(&mut BottomUpFolder { + tcx, + ty_op: |ty| match *ty.kind() { + // Replace all other mentions of the same opaque type with the hidden type, + // as the bounds must hold on the hidden type after all. + ty::Opaque(def_id2, substs2) if def_id == def_id2 && substs == substs2 => { + ty_var + } + // Instantiate nested instances of `impl Trait`. + ty::Opaque(..) => self.instantiate_opaque_types_in_map(ty), + _ => ty, + }, + lt_op: |lt| lt, + ct_op: |ct| ct, + }); + // We can't normalize associated types from `rustc_infer`, but we can eagerly register inference variables for them. let predicate = predicate.fold_with(&mut BottomUpFolder { tcx, @@ -575,10 +595,6 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { return tcx.ty_error(); } } - // Change the predicate to refer to the type variable, - // which will be the concrete type instead of the opaque type. - // This also instantiates nested instances of `impl Trait`. - let predicate = self.instantiate_opaque_types_in_map(predicate); let cause = traits::ObligationCause::new(self.value_span, self.body_id, traits::OpaqueType); diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index 8f5d6c85097..65443fd88d7 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -152,7 +152,7 @@ impl<'tcx> Elaborator<'tcx> { obligation.cause.clone(), ) }); - debug!("super_predicates: data={:?}", data); + debug!(?data, ?obligations, "super_predicates"); // Only keep those bounds that we haven't already seen. // This is necessary to prevent infinite recursion in some diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index c7823032b0c..c6145ae0d51 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -92,7 +92,8 @@ use unused::*; /// Useful for other parts of the compiler / Clippy. pub use builtin::SoftLints; -pub use context::{CheckLintNameResult, EarlyContext, LateContext, LintContext, LintStore}; +pub use context::{CheckLintNameResult, FindLintError, LintStore}; +pub use context::{EarlyContext, LateContext, LintContext}; pub use early::check_ast_crate; pub use late::check_crate; pub use passes::{EarlyLintPass, LateLintPass}; diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 27a06943cbc..c2f6118227a 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -1805,7 +1805,7 @@ declare_lint! { /// /// ### Example /// - /// ``` + /// ```rust /// if let _ = 123 { /// println!("always runs!"); /// } @@ -2431,7 +2431,19 @@ declare_lint! { /// } /// ``` /// - /// {{produces}} + /// This will produce: + /// + /// ```text + /// warning: formatting may not be suitable for sub-register argument + /// --> src/main.rs:7:19 + /// | + /// 7 | asm!("mov {0}, {0}", in(reg) 0i16); + /// | ^^^ ^^^ ---- for this argument + /// | + /// = note: `#[warn(asm_sub_register)]` on by default + /// = help: use the `x` modifier to have the register formatted as `ax` + /// = help: or use the `r` modifier to keep the default formatting of `rax` + /// ``` /// /// ### Explanation /// @@ -2470,7 +2482,17 @@ declare_lint! { /// } /// ``` /// - /// {{produces}} + /// This will produce: + /// + /// ```text + /// warning: avoid using `.att_syntax`, prefer using `options(att_syntax)` instead + /// --> src/main.rs:8:14 + /// | + /// 8 | ".att_syntax", + /// | ^^^^^^^^^^^ + /// | + /// = note: `#[warn(bad_asm_style)]` on by default + /// ``` /// /// ### Explanation /// @@ -2788,7 +2810,7 @@ declare_lint! { /// /// ### Example /// - /// ```compile_fail + /// ```rust,compile_fail /// #![feature(staged_api)] /// /// #[derive(Clone)] @@ -3618,7 +3640,17 @@ declare_lint! { /// fn foo() {} /// ``` /// - /// {{produces}} + /// This will produce: + /// + /// ```text + /// warning: duplicated attribute + /// --> src/lib.rs:2:1 + /// | + /// 2 | #[test] + /// | ^^^^^^^ + /// | + /// = note: `#[warn(duplicate_macro_attributes)]` on by default + /// ``` /// /// ### Explanation /// diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index bb9a58a0b62..85c0d28d5ea 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -21,7 +21,7 @@ use rustc_hir::definitions::{DefKey, DefPath, DefPathData, DefPathHash}; use rustc_hir::diagnostic_items::DiagnosticItems; use rustc_hir::lang_items; use rustc_index::vec::{Idx, IndexVec}; -use rustc_middle::hir::exports::Export; +use rustc_middle::metadata::ModChild; use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel}; use rustc_middle::mir::interpret::{AllocDecodingSession, AllocDecodingState}; use rustc_middle::mir::{self, Body, Promoted}; @@ -218,40 +218,40 @@ impl<'a, 'tcx> Metadata<'a, 'tcx> for (&'a MetadataBlob, &'tcx Session) { } } -impl<'a, 'tcx> Metadata<'a, 'tcx> for &'a CrateMetadataRef<'a> { +impl<'a, 'tcx> Metadata<'a, 'tcx> for CrateMetadataRef<'a> { #[inline] fn blob(self) -> &'a MetadataBlob { - &self.blob + &self.cdata.blob } #[inline] fn cdata(self) -> Option<CrateMetadataRef<'a>> { - Some(*self) + Some(self) } } -impl<'a, 'tcx> Metadata<'a, 'tcx> for (&'a CrateMetadataRef<'a>, &'tcx Session) { +impl<'a, 'tcx> Metadata<'a, 'tcx> for (CrateMetadataRef<'a>, &'tcx Session) { #[inline] fn blob(self) -> &'a MetadataBlob { - &self.0.blob + &self.0.cdata.blob } #[inline] fn cdata(self) -> Option<CrateMetadataRef<'a>> { - Some(*self.0) + Some(self.0) } #[inline] fn sess(self) -> Option<&'tcx Session> { - Some(&self.1) + Some(self.1) } } -impl<'a, 'tcx> Metadata<'a, 'tcx> for (&'a CrateMetadataRef<'a>, TyCtxt<'tcx>) { +impl<'a, 'tcx> Metadata<'a, 'tcx> for (CrateMetadataRef<'a>, TyCtxt<'tcx>) { #[inline] fn blob(self) -> &'a MetadataBlob { - &self.0.blob + &self.0.cdata.blob } #[inline] fn cdata(self) -> Option<CrateMetadataRef<'a>> { - Some(*self.0) + Some(self.0) } #[inline] fn tcx(self) -> Option<TyCtxt<'tcx>> { @@ -304,18 +304,17 @@ impl<'a, 'tcx> DecodeContext<'a, 'tcx> { &mut self, meta: T::Meta, ) -> Result<Lazy<T>, <Self as Decoder>::Error> { - let min_size = T::min_size(meta); let distance = self.read_usize()?; let position = match self.lazy_state { LazyState::NoNode => bug!("read_lazy_with_meta: outside of a metadata node"), LazyState::NodeStart(start) => { let start = start.get(); - assert!(distance + min_size <= start); - start - distance - min_size + assert!(distance <= start); + start - distance } - LazyState::Previous(last_min_end) => last_min_end.get() + distance, + LazyState::Previous(last_pos) => last_pos.get() + distance, }; - self.lazy_state = LazyState::Previous(NonZeroUsize::new(position + min_size).unwrap()); + self.lazy_state = LazyState::Previous(NonZeroUsize::new(position).unwrap()); Ok(Lazy::from_position_and_meta(NonZeroUsize::new(position).unwrap(), meta)) } @@ -415,9 +414,9 @@ impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for SyntaxContext { Ok(cdata .root .syntax_contexts - .get(&cdata, id) + .get(cdata, id) .unwrap_or_else(|| panic!("Missing SyntaxContext {:?} for crate {:?}", id, cname)) - .decode((&cdata, sess))) + .decode((cdata, sess))) }) } } @@ -443,15 +442,15 @@ impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for ExpnId { let expn_data = crate_data .root .expn_data - .get(&crate_data, index) + .get(crate_data, index) .unwrap() - .decode((&crate_data, sess)); + .decode((crate_data, sess)); let expn_hash = crate_data .root .expn_hashes - .get(&crate_data, index) + .get(crate_data, index) .unwrap() - .decode((&crate_data, sess)); + .decode((crate_data, sess)); (expn_data, expn_hash) }); Ok(expn_id) @@ -707,7 +706,7 @@ impl CrateRoot<'_> { } impl<'a, 'tcx> CrateMetadataRef<'a> { - fn raw_proc_macro(&self, id: DefIndex) -> &ProcMacro { + fn raw_proc_macro(self, id: DefIndex) -> &'a ProcMacro { // DefIndex's in root.proc_macro_data have a one-to-one correspondence // with items in 'raw_proc_macros'. let pos = self @@ -722,7 +721,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { &self.raw_proc_macros.unwrap()[pos] } - fn opt_item_ident(&self, item_index: DefIndex, sess: &Session) -> Option<Ident> { + fn opt_item_ident(self, item_index: DefIndex, sess: &Session) -> Option<Ident> { let name = self.def_key(item_index).disambiguated_data.data.get_opt_name()?; let span = match self.root.tables.ident_span.get(self, item_index) { Some(lazy_span) => lazy_span.decode((self, sess)), @@ -738,15 +737,15 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { Some(Ident::new(name, span)) } - fn item_ident(&self, item_index: DefIndex, sess: &Session) -> Ident { + fn item_ident(self, item_index: DefIndex, sess: &Session) -> Ident { self.opt_item_ident(item_index, sess).expect("no encoded ident for item") } - fn maybe_kind(&self, item_id: DefIndex) -> Option<EntryKind> { + fn maybe_kind(self, item_id: DefIndex) -> Option<EntryKind> { self.root.tables.kind.get(self, item_id).map(|k| k.decode(self)) } - fn kind(&self, item_id: DefIndex) -> EntryKind { + fn kind(self, item_id: DefIndex) -> EntryKind { self.maybe_kind(item_id).unwrap_or_else(|| { bug!( "CrateMetadata::kind({:?}): id not found, in crate {:?} with number {}", @@ -757,7 +756,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { }) } - fn def_kind(&self, item_id: DefIndex) -> DefKind { + fn def_kind(self, item_id: DefIndex) -> DefKind { self.root.tables.def_kind.get(self, item_id).map(|k| k.decode(self)).unwrap_or_else(|| { bug!( "CrateMetadata::def_kind({:?}): id not found, in crate {:?} with number {}", @@ -768,7 +767,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { }) } - fn get_span(&self, index: DefIndex, sess: &Session) -> Span { + fn get_span(self, index: DefIndex, sess: &Session) -> Span { self.root .tables .span @@ -777,7 +776,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .decode((self, sess)) } - fn load_proc_macro(&self, id: DefIndex, sess: &Session) -> SyntaxExtension { + fn load_proc_macro(self, id: DefIndex, sess: &Session) -> SyntaxExtension { let (name, kind, helper_attrs) = match *self.raw_proc_macro(id) { ProcMacro::CustomDerive { trait_name, attributes, client } => { let helper_attrs = @@ -808,7 +807,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { ) } - fn get_trait_def(&self, item_id: DefIndex, sess: &Session) -> ty::TraitDef { + fn get_trait_def(self, item_id: DefIndex, sess: &Session) -> ty::TraitDef { match self.kind(item_id) { EntryKind::Trait(data) => { let data = data.decode((self, sess)); @@ -838,7 +837,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } fn get_variant( - &self, + self, kind: &EntryKind, index: DefIndex, parent_did: DefId, @@ -863,7 +862,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { let ctor_did = data.ctor.map(|index| self.local_def_id(index)); ty::VariantDef::new( - self.item_ident(index, sess), + self.item_ident(index, sess).name, variant_did, ctor_did, data.discr, @@ -875,7 +874,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .decode(self) .map(|index| ty::FieldDef { did: self.local_def_id(index), - ident: self.item_ident(index, sess), + name: self.item_ident(index, sess).name, vis: self.get_visibility(index), }) .collect(), @@ -887,7 +886,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { ) } - fn get_adt_def(&self, item_id: DefIndex, tcx: TyCtxt<'tcx>) -> &'tcx ty::AdtDef { + fn get_adt_def(self, item_id: DefIndex, tcx: TyCtxt<'tcx>) -> &'tcx ty::AdtDef { let kind = self.kind(item_id); let did = self.local_def_id(item_id); @@ -915,7 +914,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } fn get_explicit_predicates( - &self, + self, item_id: DefIndex, tcx: TyCtxt<'tcx>, ) -> ty::GenericPredicates<'tcx> { @@ -923,7 +922,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } fn get_inferred_outlives( - &self, + self, item_id: DefIndex, tcx: TyCtxt<'tcx>, ) -> &'tcx [(ty::Predicate<'tcx>, Span)] { @@ -936,7 +935,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } fn get_super_predicates( - &self, + self, item_id: DefIndex, tcx: TyCtxt<'tcx>, ) -> ty::GenericPredicates<'tcx> { @@ -944,7 +943,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } fn get_explicit_item_bounds( - &self, + self, item_id: DefIndex, tcx: TyCtxt<'tcx>, ) -> &'tcx [(ty::Predicate<'tcx>, Span)] { @@ -956,11 +955,11 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .unwrap_or_default() } - fn get_generics(&self, item_id: DefIndex, sess: &Session) -> ty::Generics { + fn get_generics(self, item_id: DefIndex, sess: &Session) -> ty::Generics { self.root.tables.generics.get(self, item_id).unwrap().decode((self, sess)) } - fn get_type(&self, id: DefIndex, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { + fn get_type(self, id: DefIndex, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { self.root .tables .ty @@ -969,59 +968,63 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .decode((self, tcx)) } - fn get_stability(&self, id: DefIndex) -> Option<attr::Stability> { + fn get_stability(self, id: DefIndex) -> Option<attr::Stability> { self.root.tables.stability.get(self, id).map(|stab| stab.decode(self)) } - fn get_const_stability(&self, id: DefIndex) -> Option<attr::ConstStability> { + fn get_const_stability(self, id: DefIndex) -> Option<attr::ConstStability> { self.root.tables.const_stability.get(self, id).map(|stab| stab.decode(self)) } - fn get_deprecation(&self, id: DefIndex) -> Option<attr::Deprecation> { + fn get_deprecation(self, id: DefIndex) -> Option<attr::Deprecation> { self.root.tables.deprecation.get(self, id).map(|depr| depr.decode(self)) } - fn get_visibility(&self, id: DefIndex) -> ty::Visibility { + fn get_visibility(self, id: DefIndex) -> ty::Visibility { self.root.tables.visibility.get(self, id).unwrap().decode(self) } - fn get_impl_data(&self, id: DefIndex) -> ImplData { + fn get_impl_data(self, id: DefIndex) -> ImplData { match self.kind(id) { EntryKind::Impl(data) => data.decode(self), _ => bug!(), } } - fn get_parent_impl(&self, id: DefIndex) -> Option<DefId> { + fn get_parent_impl(self, id: DefIndex) -> Option<DefId> { self.get_impl_data(id).parent_impl } - fn get_impl_polarity(&self, id: DefIndex) -> ty::ImplPolarity { + fn get_impl_polarity(self, id: DefIndex) -> ty::ImplPolarity { self.get_impl_data(id).polarity } - fn get_impl_defaultness(&self, id: DefIndex) -> hir::Defaultness { + fn get_impl_defaultness(self, id: DefIndex) -> hir::Defaultness { self.get_impl_data(id).defaultness } - fn get_impl_constness(&self, id: DefIndex) -> hir::Constness { + fn get_impl_constness(self, id: DefIndex) -> hir::Constness { self.get_impl_data(id).constness } - fn get_coerce_unsized_info(&self, id: DefIndex) -> Option<ty::adjustment::CoerceUnsizedInfo> { + fn get_trait_item_def_id(self, id: DefIndex) -> Option<DefId> { + self.root.tables.trait_item_def_id.get(self, id).map(|d| d.decode(self)) + } + + fn get_coerce_unsized_info(self, id: DefIndex) -> Option<ty::adjustment::CoerceUnsizedInfo> { self.get_impl_data(id).coerce_unsized_info } - fn get_impl_trait(&self, id: DefIndex, tcx: TyCtxt<'tcx>) -> Option<ty::TraitRef<'tcx>> { + fn get_impl_trait(self, id: DefIndex, tcx: TyCtxt<'tcx>) -> Option<ty::TraitRef<'tcx>> { self.root.tables.impl_trait_ref.get(self, id).map(|tr| tr.decode((self, tcx))) } - fn get_expn_that_defined(&self, id: DefIndex, sess: &Session) -> ExpnId { + fn get_expn_that_defined(self, id: DefIndex, sess: &Session) -> ExpnId { self.root.tables.expn_that_defined.get(self, id).unwrap().decode((self, sess)) } fn get_const_param_default( - &self, + self, tcx: TyCtxt<'tcx>, id: DefIndex, ) -> rustc_middle::ty::Const<'tcx> { @@ -1029,14 +1032,14 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } /// Iterates over all the stability attributes in the given crate. - fn get_lib_features(&self, tcx: TyCtxt<'tcx>) -> &'tcx [(Symbol, Option<Symbol>)] { + fn get_lib_features(self, tcx: TyCtxt<'tcx>) -> &'tcx [(Symbol, Option<Symbol>)] { // FIXME: For a proc macro crate, not sure whether we should return the "host" // features or an empty Vec. Both don't cause ICEs. tcx.arena.alloc_from_iter(self.root.lib_features.decode(self)) } /// Iterates over the language items in the given crate. - fn get_lang_items(&self, tcx: TyCtxt<'tcx>) -> &'tcx [(DefId, usize)] { + fn get_lang_items(self, tcx: TyCtxt<'tcx>) -> &'tcx [(DefId, usize)] { if self.root.is_proc_macro_crate() { // Proc macro crates do not export any lang-items to the target. &[] @@ -1051,7 +1054,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } /// Iterates over the diagnostic items in the given crate. - fn get_diagnostic_items(&self) -> DiagnosticItems { + fn get_diagnostic_items(self) -> DiagnosticItems { if self.root.is_proc_macro_crate() { // Proc macro crates do not export any diagnostic-items to the target. Default::default() @@ -1071,33 +1074,38 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } } - /// Iterates over each child of the given item. - fn each_child_of_item(&self, id: DefIndex, mut callback: impl FnMut(Export), sess: &Session) { + /// Iterates over all named children of the given module, + /// including both proper items and reexports. + /// Module here is understood in name resolution sense - it can be a `mod` item, + /// or a crate root, or an enum, or a trait. + fn for_each_module_child( + self, + id: DefIndex, + mut callback: impl FnMut(ModChild), + sess: &Session, + ) { if let Some(data) = &self.root.proc_macro_data { - /* If we are loading as a proc macro, we want to return the view of this crate - * as a proc macro crate. - */ + // If we are loading as a proc macro, we want to return + // the view of this crate as a proc macro crate. if id == CRATE_DEF_INDEX { - let macros = data.macros.decode(self); - for def_index in macros { + for def_index in data.macros.decode(self) { let raw_macro = self.raw_proc_macro(def_index); let res = Res::Def( DefKind::Macro(macro_kind(raw_macro)), self.local_def_id(def_index), ); let ident = self.item_ident(def_index, sess); - callback(Export { ident, res, vis: ty::Visibility::Public, span: ident.span }); + callback(ModChild { + ident, + res, + vis: ty::Visibility::Public, + span: ident.span, + }); } } return; } - // Find the item. - let kind = match self.maybe_kind(id) { - None => return, - Some(kind) => kind, - }; - // Iterate over all children. if let Some(children) = self.root.tables.children.get(self, id) { for child_index in children.decode((self, sess)) { @@ -1113,7 +1121,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { let vis = self.get_visibility(child_index); let span = self.get_span(child_index, sess); - callback(Export { ident, res, vis, span }); + callback(ModChild { ident, res, vis, span }); // For non-re-export structs and variants add their constructors to children. // Re-export lists automatically contain constructors when necessary. @@ -1125,7 +1133,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { let ctor_res = Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id); let vis = self.get_visibility(ctor_def_id.index); - callback(Export { res: ctor_res, vis, ident, span }); + callback(ModChild { ident, res: ctor_res, vis, span }); } } DefKind::Variant => { @@ -1150,7 +1158,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { vis = ty::Visibility::Restricted(crate_def_id); } } - callback(Export { res: ctor_res, ident, vis, span }); + callback(ModChild { ident, res: ctor_res, vis, span }); } _ => {} } @@ -1158,22 +1166,26 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } } - if let EntryKind::Mod(exports) = kind { - for exp in exports.decode((self, sess)) { - callback(exp); + match self.kind(id) { + EntryKind::Mod(exports) => { + for exp in exports.decode((self, sess)) { + callback(exp); + } } + EntryKind::Enum(..) | EntryKind::Trait(..) => {} + _ => bug!("`for_each_module_child` is called on a non-module: {:?}", self.def_kind(id)), } } - fn is_ctfe_mir_available(&self, id: DefIndex) -> bool { + fn is_ctfe_mir_available(self, id: DefIndex) -> bool { self.root.tables.mir_for_ctfe.get(self, id).is_some() } - fn is_item_mir_available(&self, id: DefIndex) -> bool { + fn is_item_mir_available(self, id: DefIndex) -> bool { self.root.tables.mir.get(self, id).is_some() } - fn module_expansion(&self, id: DefIndex, sess: &Session) -> ExpnId { + fn module_expansion(self, id: DefIndex, sess: &Session) -> ExpnId { match self.kind(id) { EntryKind::Mod(_) | EntryKind::Enum(_) | EntryKind::Trait(_) => { self.get_expn_that_defined(id, sess) @@ -1182,7 +1194,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } } - fn get_optimized_mir(&self, tcx: TyCtxt<'tcx>, id: DefIndex) -> Body<'tcx> { + fn get_optimized_mir(self, tcx: TyCtxt<'tcx>, id: DefIndex) -> Body<'tcx> { self.root .tables .mir @@ -1193,7 +1205,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .decode((self, tcx)) } - fn get_mir_for_ctfe(&self, tcx: TyCtxt<'tcx>, id: DefIndex) -> Body<'tcx> { + fn get_mir_for_ctfe(self, tcx: TyCtxt<'tcx>, id: DefIndex) -> Body<'tcx> { self.root .tables .mir_for_ctfe @@ -1205,7 +1217,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } fn get_thir_abstract_const( - &self, + self, tcx: TyCtxt<'tcx>, id: DefIndex, ) -> Result<Option<&'tcx [thir::abstract_const::Node<'tcx>]>, ErrorReported> { @@ -1216,7 +1228,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .map_or(Ok(None), |v| Ok(Some(v.decode((self, tcx))))) } - fn get_unused_generic_params(&self, id: DefIndex) -> FiniteBitSet<u32> { + fn get_unused_generic_params(self, id: DefIndex) -> FiniteBitSet<u32> { self.root .tables .unused_generic_params @@ -1225,7 +1237,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .unwrap_or_default() } - fn get_promoted_mir(&self, tcx: TyCtxt<'tcx>, id: DefIndex) -> IndexVec<Promoted, Body<'tcx>> { + fn get_promoted_mir(self, tcx: TyCtxt<'tcx>, id: DefIndex) -> IndexVec<Promoted, Body<'tcx>> { self.root .tables .promoted_mir @@ -1236,7 +1248,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .decode((self, tcx)) } - fn mir_const_qualif(&self, id: DefIndex) -> mir::ConstQualifs { + fn mir_const_qualif(self, id: DefIndex) -> mir::ConstQualifs { match self.kind(id) { EntryKind::AnonConst(qualif, _) | EntryKind::Const(qualif, _) @@ -1251,14 +1263,24 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } } - fn get_fn_has_self_parameter(&self, id: DefIndex) -> bool { + fn get_fn_has_self_parameter(self, id: DefIndex) -> bool { match self.kind(id) { EntryKind::AssocFn(data) => data.decode(self).has_self, _ => false, } } - fn get_associated_item(&self, id: DefIndex, sess: &Session) -> ty::AssocItem { + fn get_associated_item_def_ids(self, tcx: TyCtxt<'tcx>, id: DefIndex) -> &'tcx [DefId] { + if let Some(children) = self.root.tables.children.get(self, id) { + tcx.arena.alloc_from_iter( + children.decode((self, tcx.sess)).map(|child_index| self.local_def_id(child_index)), + ) + } else { + &[] + } + } + + fn get_associated_item(self, id: DefIndex, sess: &Session) -> ty::AssocItem { let def_key = self.def_key(id); let parent = self.local_def_id(def_key.parent.unwrap()); let ident = self.item_ident(id, sess); @@ -1279,16 +1301,17 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { vis: self.get_visibility(id), defaultness: container.defaultness(), def_id: self.local_def_id(id), + trait_item_def_id: self.get_trait_item_def_id(id), container: container.with_def_id(parent), fn_has_self_parameter: has_self, } } - fn get_item_variances(&'a self, id: DefIndex) -> impl Iterator<Item = ty::Variance> + 'a { + fn get_item_variances(self, id: DefIndex) -> impl Iterator<Item = ty::Variance> + 'a { self.root.tables.variances.get(self, id).unwrap_or_else(Lazy::empty).decode(self) } - fn get_ctor_def_id_and_kind(&self, node_id: DefIndex) -> Option<(DefId, CtorKind)> { + fn get_ctor_def_id_and_kind(self, node_id: DefIndex) -> Option<(DefId, CtorKind)> { match self.kind(node_id) { EntryKind::Struct(data, _) | EntryKind::Variant(data) => { let vdata = data.decode(self); @@ -1299,7 +1322,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } fn get_item_attrs( - &'a self, + self, id: DefIndex, sess: &'a Session, ) -> impl Iterator<Item = ast::Attribute> + 'a { @@ -1323,7 +1346,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .decode((self, sess)) } - fn get_struct_field_names(&self, id: DefIndex, sess: &Session) -> Vec<Spanned<Symbol>> { + fn get_struct_field_names(self, id: DefIndex, sess: &Session) -> Vec<Spanned<Symbol>> { self.root .tables .children @@ -1334,7 +1357,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .collect() } - fn get_struct_field_visibilities(&self, id: DefIndex) -> Vec<Visibility> { + fn get_struct_field_visibilities(self, id: DefIndex) -> Vec<Visibility> { self.root .tables .children @@ -1346,7 +1369,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } fn get_inherent_implementations_for_type( - &self, + self, tcx: TyCtxt<'tcx>, id: DefIndex, ) -> &'tcx [DefId] { @@ -1361,20 +1384,20 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { ) } - fn get_traits(&'a self) -> impl Iterator<Item = DefId> + 'a { - self.root.traits.decode(self).map(|index| self.local_def_id(index)) + fn get_traits(self) -> impl Iterator<Item = DefId> + 'a { + self.root.traits.decode(self).map(move |index| self.local_def_id(index)) } - fn get_trait_impls(&'a self) -> impl Iterator<Item = (DefId, Option<SimplifiedType>)> + 'a { - self.trait_impls.values().flat_map(move |impls| { + fn get_trait_impls(self) -> impl Iterator<Item = (DefId, Option<SimplifiedType>)> + 'a { + self.cdata.trait_impls.values().flat_map(move |impls| { impls .decode(self) - .map(|(idx, simplified_self_ty)| (self.local_def_id(idx), simplified_self_ty)) + .map(move |(idx, simplified_self_ty)| (self.local_def_id(idx), simplified_self_ty)) }) } fn get_implementations_of_trait( - &self, + self, tcx: TyCtxt<'tcx>, trait_def_id: DefId, ) -> &'tcx [(DefId, Option<SimplifiedType>)] { @@ -1401,7 +1424,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } } - fn get_trait_of_item(&self, id: DefIndex) -> Option<DefId> { + fn get_trait_of_item(self, id: DefIndex) -> Option<DefId> { let def_key = self.def_key(id); match def_key.disambiguated_data.data { DefPathData::TypeNs(..) | DefPathData::ValueNs(..) => (), @@ -1414,7 +1437,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { }) } - fn get_native_libraries(&self, sess: &Session) -> Vec<NativeLib> { + fn get_native_libraries(self, sess: &Session) -> Vec<NativeLib> { if self.root.is_proc_macro_crate() { // Proc macro crates do not have any *target* native libraries. vec![] @@ -1423,7 +1446,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } } - fn get_proc_macro_quoted_span(&self, index: usize, sess: &Session) -> Span { + fn get_proc_macro_quoted_span(self, index: usize, sess: &Session) -> Span { self.root .tables .proc_macro_quoted_spans @@ -1432,7 +1455,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .decode((self, sess)) } - fn get_foreign_modules(&self, tcx: TyCtxt<'tcx>) -> Lrc<FxHashMap<DefId, ForeignModule>> { + fn get_foreign_modules(self, tcx: TyCtxt<'tcx>) -> Lrc<FxHashMap<DefId, ForeignModule>> { if self.root.is_proc_macro_crate() { // Proc macro crates do not have any *target* foreign modules. Lrc::new(FxHashMap::default()) @@ -1444,7 +1467,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } fn get_dylib_dependency_formats( - &self, + self, tcx: TyCtxt<'tcx>, ) -> &'tcx [(CrateNum, LinkagePreference)] { tcx.arena.alloc_from_iter( @@ -1455,7 +1478,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { ) } - fn get_missing_lang_items(&self, tcx: TyCtxt<'tcx>) -> &'tcx [lang_items::LangItem] { + fn get_missing_lang_items(self, tcx: TyCtxt<'tcx>) -> &'tcx [lang_items::LangItem] { if self.root.is_proc_macro_crate() { // Proc macro crates do not depend on any target weak lang-items. &[] @@ -1464,7 +1487,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } } - fn get_fn_param_names(&self, tcx: TyCtxt<'tcx>, id: DefIndex) -> &'tcx [Ident] { + fn get_fn_param_names(self, tcx: TyCtxt<'tcx>, id: DefIndex) -> &'tcx [Ident] { let param_names = match self.kind(id) { EntryKind::Fn(data) | EntryKind::ForeignFn(data) => data.decode(self).param_names, EntryKind::AssocFn(data) => data.decode(self).fn_data.param_names, @@ -1474,7 +1497,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } fn exported_symbols( - &self, + self, tcx: TyCtxt<'tcx>, ) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportLevel)] { if self.root.is_proc_macro_crate() { @@ -1486,7 +1509,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } } - fn get_rendered_const(&self, id: DefIndex) -> String { + fn get_rendered_const(self, id: DefIndex) -> String { match self.kind(id) { EntryKind::AnonConst(_, data) | EntryKind::Const(_, data) @@ -1495,7 +1518,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } } - fn get_macro(&self, id: DefIndex, sess: &Session) -> MacroDef { + fn get_macro(self, id: DefIndex, sess: &Session) -> MacroDef { match self.kind(id) { EntryKind::MacroDef(macro_def) => macro_def.decode((self, sess)), _ => bug!(), @@ -1504,7 +1527,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { // This replicates some of the logic of the crate-local `is_const_fn_raw` query, because we // don't serialize constness for tuple variant and tuple struct constructors. - fn is_const_fn_raw(&self, id: DefIndex) -> bool { + fn is_const_fn_raw(self, id: DefIndex) -> bool { let constness = match self.kind(id) { EntryKind::AssocFn(data) => data.decode(self).fn_data.constness, EntryKind::Fn(data) => data.decode(self).constness, @@ -1515,7 +1538,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { constness == hir::Constness::Const } - fn asyncness(&self, id: DefIndex) -> hir::IsAsync { + fn asyncness(self, id: DefIndex) -> hir::IsAsync { match self.kind(id) { EntryKind::Fn(data) => data.decode(self).asyncness, EntryKind::AssocFn(data) => data.decode(self).fn_data.asyncness, @@ -1524,7 +1547,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } } - fn is_foreign_item(&self, id: DefIndex) -> bool { + fn is_foreign_item(self, id: DefIndex) -> bool { match self.kind(id) { EntryKind::ForeignImmStatic | EntryKind::ForeignMutStatic | EntryKind::ForeignFn(_) => { true @@ -1533,7 +1556,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } } - fn static_mutability(&self, id: DefIndex) -> Option<hir::Mutability> { + fn static_mutability(self, id: DefIndex) -> Option<hir::Mutability> { match self.kind(id) { EntryKind::ImmStatic | EntryKind::ForeignImmStatic => Some(hir::Mutability::Not), EntryKind::MutStatic | EntryKind::ForeignMutStatic => Some(hir::Mutability::Mut), @@ -1541,19 +1564,19 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } } - fn generator_kind(&self, id: DefIndex) -> Option<hir::GeneratorKind> { + fn generator_kind(self, id: DefIndex) -> Option<hir::GeneratorKind> { match self.kind(id) { EntryKind::Generator(data) => Some(data), _ => None, } } - fn fn_sig(&self, id: DefIndex, tcx: TyCtxt<'tcx>) -> ty::PolyFnSig<'tcx> { + fn fn_sig(self, id: DefIndex, tcx: TyCtxt<'tcx>) -> ty::PolyFnSig<'tcx> { self.root.tables.fn_sig.get(self, id).unwrap().decode((self, tcx)) } #[inline] - fn def_key(&self, index: DefIndex) -> DefKey { + fn def_key(self, index: DefIndex) -> DefKey { *self .def_key_cache .lock() @@ -1562,13 +1585,13 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } // Returns the path leading to the thing with this `id`. - fn def_path(&self, id: DefIndex) -> DefPath { + fn def_path(self, id: DefIndex) -> DefPath { debug!("def_path(cnum={:?}, id={:?})", self.cnum, id); DefPath::make(self.cnum, id, |parent| self.def_key(parent)) } fn def_path_hash_unlocked( - &self, + self, index: DefIndex, def_path_hashes: &mut FxHashMap<DefIndex, DefPathHash>, ) -> DefPathHash { @@ -1578,17 +1601,17 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } #[inline] - fn def_path_hash(&self, index: DefIndex) -> DefPathHash { + fn def_path_hash(self, index: DefIndex) -> DefPathHash { let mut def_path_hashes = self.def_path_hash_cache.lock(); self.def_path_hash_unlocked(index, &mut def_path_hashes) } #[inline] - fn def_path_hash_to_def_index(&self, hash: DefPathHash) -> DefIndex { + fn def_path_hash_to_def_index(self, hash: DefPathHash) -> DefIndex { self.def_path_hash_map.def_path_hash_to_def_index(&hash) } - fn expn_hash_to_expn_id(&self, sess: &Session, index_guess: u32, hash: ExpnHash) -> ExpnId { + fn expn_hash_to_expn_id(self, sess: &Session, index_guess: u32, hash: ExpnHash) -> ExpnId { debug_assert_eq!(ExpnId::from_hash(hash), None); let index_guess = ExpnIndex::from_u32(index_guess); let old_hash = self.root.expn_hashes.get(self, index_guess).map(|lazy| lazy.decode(self)); @@ -1646,7 +1669,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { /// /// Proc macro crates don't currently export spans, so this function does not have /// to work for them. - fn imported_source_files(&self, sess: &Session) -> &'a [ImportedSourceFile] { + fn imported_source_files(self, sess: &Session) -> &'a [ImportedSourceFile] { // Translate the virtual `/rustc/$hash` prefix back to a real directory // that should hold actual sources, where possible. // diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index aac0aa61ea6..a639772fde7 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -4,12 +4,13 @@ use crate::native_libs; use rustc_ast as ast; use rustc_data_structures::stable_map::FxHashMap; -use rustc_hir::def::{CtorKind, DefKind}; +use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; -use rustc_middle::hir::exports::Export; +use rustc_middle::metadata::ModChild; use rustc_middle::middle::exported_symbols::ExportedSymbol; use rustc_middle::middle::stability::DeprecationEntry; +use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::query::{ExternProviders, Providers}; use rustc_middle::ty::{self, TyCtxt, Visibility}; use rustc_session::cstore::{CrateSource, CrateStore, ForeignModule}; @@ -103,12 +104,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, tcx.calculate_dtor(def_id, |_,_| Ok(())) } variances_of => { tcx.arena.alloc_from_iter(cdata.get_item_variances(def_id.index)) } - associated_item_def_ids => { - let mut result = SmallVec::<[_; 8]>::new(); - cdata.each_child_of_item(def_id.index, - |child| result.push(child.res.def_id()), tcx.sess); - tcx.arena.alloc_slice(&result) - } + associated_item_def_ids => { cdata.get_associated_item_def_ids(tcx, def_id.index) } associated_item => { cdata.get_associated_item(def_id.index, tcx.sess) } impl_trait_ref => { cdata.get_impl_trait(def_id.index, tcx) } impl_polarity => { cdata.get_impl_polarity(def_id.index) } @@ -192,8 +188,6 @@ provide! { <'tcx> tcx, def_id, other, cdata, extra_filename => { cdata.root.extra_filename.clone() } traits_in_crate => { tcx.arena.alloc_from_iter(cdata.get_traits()) } - all_trait_implementations => { tcx.arena.alloc_from_iter(cdata.get_trait_impls()) } - implementations_of_trait => { cdata.get_implementations_of_trait(tcx, other) } visibility => { cdata.get_visibility(def_id.index) } @@ -201,9 +195,9 @@ provide! { <'tcx> tcx, def_id, other, cdata, let r = *cdata.dep_kind.lock(); r } - item_children => { + module_children => { let mut result = SmallVec::<[_; 8]>::new(); - cdata.each_child_of_item(def_id.index, |child| result.push(child), tcx.sess); + cdata.for_each_module_child(def_id.index, |child| result.push(child), tcx.sess); tcx.arena.alloc_slice(&result) } defined_lib_features => { cdata.get_lib_features(tcx) } @@ -314,35 +308,40 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { bfs_queue.push_back(DefId { krate: cnum, index: CRATE_DEF_INDEX }); } - let mut add_child = |bfs_queue: &mut VecDeque<_>, export: &Export, parent: DefId| { - if !export.vis.is_public() { + let mut add_child = |bfs_queue: &mut VecDeque<_>, child: &ModChild, parent: DefId| { + if !child.vis.is_public() { return; } - if let Some(child) = export.res.opt_def_id() { - if export.ident.name == kw::Underscore { - fallback_map.insert(child, parent); + if let Some(def_id) = child.res.opt_def_id() { + if child.ident.name == kw::Underscore { + fallback_map.insert(def_id, parent); return; } - match visible_parent_map.entry(child) { + match visible_parent_map.entry(def_id) { Entry::Occupied(mut entry) => { // If `child` is defined in crate `cnum`, ensure // that it is mapped to a parent in `cnum`. - if child.is_local() && entry.get().is_local() { + if def_id.is_local() && entry.get().is_local() { entry.insert(parent); } } Entry::Vacant(entry) => { entry.insert(parent); - bfs_queue.push_back(child); + if matches!( + child.res, + Res::Def(DefKind::Mod | DefKind::Enum | DefKind::Trait, _) + ) { + bfs_queue.push_back(def_id); + } } } } }; while let Some(def) = bfs_queue.pop_front() { - for child in tcx.item_children(def).iter() { + for child in tcx.module_children(def).iter() { add_child(bfs_queue, child, def); } } @@ -353,7 +352,7 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { visible_parent_map.entry(child).or_insert(parent); } - visible_parent_map + Lrc::new(visible_parent_map) }, dependency_formats: |tcx, ()| Lrc::new(crate::dependency_format::calculate(tcx)), @@ -388,9 +387,9 @@ impl CStore { self.get_crate_data(def.krate).get_visibility(def.index) } - pub fn item_children_untracked(&self, def_id: DefId, sess: &Session) -> Vec<Export> { + pub fn module_children_untracked(&self, def_id: DefId, sess: &Session) -> Vec<ModChild> { let mut result = vec![]; - self.get_crate_data(def_id.krate).each_child_of_item( + self.get_crate_data(def_id.krate).for_each_module_child( def_id.index, |child| result.push(child), sess, @@ -473,6 +472,17 @@ impl CStore { ) -> Span { self.get_crate_data(cnum).get_proc_macro_quoted_span(id, sess) } + + pub fn traits_in_crate_untracked(&self, cnum: CrateNum) -> Vec<DefId> { + self.get_crate_data(cnum).get_traits().collect() + } + + pub fn trait_impls_in_crate_untracked( + &self, + cnum: CrateNum, + ) -> Vec<(DefId, Option<SimplifiedType>)> { + self.get_crate_data(cnum).get_trait_impls().collect() + } } impl CrateStore for CStore { diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 12d66f4fc45..fa1752aaec3 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -404,24 +404,24 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { &mut self, lazy: Lazy<T>, ) -> Result<(), <Self as Encoder>::Error> { - let min_end = lazy.position.get() + T::min_size(lazy.meta); + let pos = lazy.position.get(); let distance = match self.lazy_state { LazyState::NoNode => bug!("emit_lazy_distance: outside of a metadata node"), LazyState::NodeStart(start) => { let start = start.get(); - assert!(min_end <= start); - start - min_end + assert!(pos <= start); + start - pos } - LazyState::Previous(last_min_end) => { + LazyState::Previous(last_pos) => { assert!( - last_min_end <= lazy.position, + last_pos <= lazy.position, "make sure that the calls to `lazy*` \ are in the same order as the metadata fields", ); - lazy.position.get() - last_min_end.get() + lazy.position.get() - last_pos.get() } }; - self.lazy_state = LazyState::Previous(NonZeroUsize::new(min_end).unwrap()); + self.lazy_state = LazyState::Previous(NonZeroUsize::new(pos).unwrap()); self.emit_usize(distance) } @@ -436,7 +436,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let meta = value.encode_contents_for_lazy(self); self.lazy_state = LazyState::NoNode; - assert!(pos.get() + <T>::min_size(meta) <= self.position()); + assert!(pos.get() <= self.position()); Lazy::from_position_and_meta(pos, meta) } @@ -1052,7 +1052,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { assert!(f.did.is_local()); f.did.index })); - self.encode_ident_span(def_id, variant.ident); + self.encode_ident_span(def_id, variant.ident(tcx)); self.encode_item_type(def_id); if variant.ctor_kind == CtorKind::Fn { // FIXME(eddyb) encode signature only in `encode_enum_variant_ctor`. @@ -1094,7 +1094,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // code uses it). However, we skip encoding anything relating to child // items - we encode information about proc-macros later on. let reexports = if !self.is_proc_macro { - match tcx.module_exports(local_def_id) { + match tcx.module_reexports(local_def_id) { Some(exports) => self.lazy(exports), _ => Lazy::empty(), } @@ -1104,7 +1104,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { record!(self.tables.kind[def_id] <- EntryKind::Mod(reexports)); if self.is_proc_macro { - record!(self.tables.children[def_id] <- &[]); // Encode this here because we don't do it in encode_def_ids. record!(self.tables.expn_that_defined[def_id] <- tcx.expn_that_defined(local_def_id)); } else { @@ -1139,7 +1138,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { debug!("EncodeContext::encode_field({:?})", def_id); record!(self.tables.kind[def_id] <- EntryKind::Field); - self.encode_ident_span(def_id, field.ident); + self.encode_ident_span(def_id, field.ident(self.tcx)); self.encode_item_type(def_id); } @@ -1294,6 +1293,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } self.encode_ident_span(def_id, impl_item.ident); self.encode_item_type(def_id); + if let Some(trait_item_def_id) = impl_item.trait_item_def_id { + record!(self.tables.trait_item_def_id[def_id] <- trait_item_def_id); + } if impl_item.kind == ty::AssocKind::Fn { record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); } diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 4076e0b9e0f..75c5880f05d 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -12,7 +12,7 @@ use rustc_hir::def_id::{DefId, DefIndex, DefPathHash, StableCrateId}; use rustc_hir::definitions::DefKey; use rustc_hir::lang_items; use rustc_index::{bit_set::FiniteBitSet, vec::IndexVec}; -use rustc_middle::hir::exports::Export; +use rustc_middle::metadata::ModChild; use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel}; use rustc_middle::mir; use rustc_middle::thir; @@ -63,27 +63,14 @@ pub const METADATA_HEADER: &[u8] = &[b'r', b'u', b's', b't', 0, 0, 0, METADATA_V /// e.g. for `Lazy<[T]>`, this is the length (count of `T` values). trait LazyMeta { type Meta: Copy + 'static; - - /// Returns the minimum encoded size. - // FIXME(eddyb) Give better estimates for certain types. - fn min_size(meta: Self::Meta) -> usize; } impl<T> LazyMeta for T { type Meta = (); - - fn min_size(_: ()) -> usize { - assert_ne!(std::mem::size_of::<T>(), 0); - 1 - } } impl<T> LazyMeta for [T] { type Meta = usize; - - fn min_size(len: usize) -> usize { - len * T::min_size(()) - } } /// A value of type T referred to by its absolute position @@ -161,8 +148,7 @@ enum LazyState { NodeStart(NonZeroUsize), /// Inside a metadata node, with a previous `Lazy`. - /// The position is a conservative estimate of where that - /// previous `Lazy` would end (see their comments). + /// The position is where that previous `Lazy` would start. Previous(NonZeroUsize), } @@ -302,6 +288,7 @@ define_tables! { ty: Table<DefIndex, Lazy!(Ty<'tcx>)>, fn_sig: Table<DefIndex, Lazy!(ty::PolyFnSig<'tcx>)>, impl_trait_ref: Table<DefIndex, Lazy!(ty::TraitRef<'tcx>)>, + trait_item_def_id: Table<DefIndex, Lazy<DefId>>, inherent_impls: Table<DefIndex, Lazy<[DefIndex]>>, variances: Table<DefIndex, Lazy<[ty::Variance]>>, generics: Table<DefIndex, Lazy<ty::Generics>>, @@ -349,7 +336,7 @@ enum EntryKind { Union(Lazy<VariantData>, ReprOptions), Fn(Lazy<FnData>), ForeignFn(Lazy<FnData>), - Mod(Lazy<[Export]>), + Mod(Lazy<[ModChild]>), MacroDef(Lazy<MacroDef>), ProcMacro(MacroKind), Closure, diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index 4dfefda490b..265ca5a6d8d 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -183,10 +183,6 @@ where Option<T>: FixedSizeEncoding, { type Meta = usize; - - fn min_size(len: usize) -> usize { - len - } } impl<I: Idx, T> Lazy<Table<I, T>> diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs index 79d7ca32f35..cf50378ad60 100644 --- a/compiler/rustc_middle/src/dep_graph/mod.rs +++ b/compiler/rustc_middle/src/dep_graph/mod.rs @@ -1,6 +1,5 @@ use crate::ty::{self, TyCtxt}; use rustc_data_structures::profiling::SelfProfilerRef; -use rustc_data_structures::sync::Lock; use rustc_query_system::ich::StableHashingContext; use rustc_session::Session; @@ -17,6 +16,7 @@ crate use dep_node::{make_compile_codegen_unit, make_compile_mono_item}; pub type DepGraph = rustc_query_system::dep_graph::DepGraph<DepKind>; pub type TaskDeps = rustc_query_system::dep_graph::TaskDeps<DepKind>; +pub type TaskDepsRef<'a> = rustc_query_system::dep_graph::TaskDepsRef<'a, DepKind>; pub type DepGraphQuery = rustc_query_system::dep_graph::DepGraphQuery<DepKind>; pub type SerializedDepGraph = rustc_query_system::dep_graph::SerializedDepGraph<DepKind>; pub type EdgeFilter = rustc_query_system::dep_graph::debug::EdgeFilter<DepKind>; @@ -45,7 +45,7 @@ impl rustc_query_system::dep_graph::DepKind for DepKind { write!(f, ")") } - fn with_deps<OP, R>(task_deps: Option<&Lock<TaskDeps>>, op: OP) -> R + fn with_deps<OP, R>(task_deps: TaskDepsRef<'_>, op: OP) -> R where OP: FnOnce() -> R, { @@ -58,7 +58,7 @@ impl rustc_query_system::dep_graph::DepKind for DepKind { fn read_deps<OP>(op: OP) where - OP: for<'a> FnOnce(Option<&'a Lock<TaskDeps>>), + OP: for<'a> FnOnce(TaskDepsRef<'a>), { ty::tls::with_context_opt(|icx| { let icx = if let Some(icx) = icx { icx } else { return }; diff --git a/compiler/rustc_middle/src/hir/exports.rs b/compiler/rustc_middle/src/hir/exports.rs deleted file mode 100644 index f37b976fba6..00000000000 --- a/compiler/rustc_middle/src/hir/exports.rs +++ /dev/null @@ -1,28 +0,0 @@ -use crate::ty; - -use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def::Res; -use rustc_hir::def_id::LocalDefId; -use rustc_macros::HashStable; -use rustc_span::symbol::Ident; -use rustc_span::Span; - -use std::fmt::Debug; - -/// This is the replacement export map. It maps a module to all of the exports -/// within. -pub type ExportMap = FxHashMap<LocalDefId, Vec<Export>>; - -#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] -pub struct Export { - /// The name of the target. - pub ident: Ident, - /// The resolution of the target. - /// Local variables cannot be exported, so this `Res` doesn't need the ID parameter. - pub res: Res<!>, - /// The span of the target. - pub span: Span, - /// The visibility of the export. - /// We include non-`pub` exports for hygienic macros that get used from extern crates. - pub vis: ty::Visibility, -} diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 95d7273b17b..557dc25528f 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -2,7 +2,6 @@ //! //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html -pub mod exports; pub mod map; pub mod place; diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index e6dd4e484cc..920eca7a717 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -84,6 +84,7 @@ pub mod dep_graph; pub mod hir; pub mod infer; pub mod lint; +pub mod metadata; pub mod middle; pub mod mir; pub mod thir; diff --git a/compiler/rustc_middle/src/metadata.rs b/compiler/rustc_middle/src/metadata.rs new file mode 100644 index 00000000000..6dcdc58c72d --- /dev/null +++ b/compiler/rustc_middle/src/metadata.rs @@ -0,0 +1,24 @@ +use crate::ty; + +use rustc_hir::def::Res; +use rustc_macros::HashStable; +use rustc_span::symbol::Ident; +use rustc_span::Span; + +/// This structure is supposed to keep enough data to re-create `NameBinding`s for other crates +/// during name resolution. Right now the bindings are not recreated entirely precisely so we may +/// need to add more data in the future to correctly support macros 2.0, for example. +/// Module child can be either a proper item or a reexport (including private imports). +/// In case of reexport all the fields describe the reexport item itself, not what it refers to. +#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] +pub struct ModChild { + /// Name of the item. + pub ident: Ident, + /// Resolution result corresponding to the item. + /// Local variables cannot be exported, so this `Res` doesn't need the ID parameter. + pub res: Res<!>, + /// Visibility of the item. + pub vis: ty::Visibility, + /// Span of the item. + pub span: Span, +} diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs index f33bd3438b9..ff993ac392c 100644 --- a/compiler/rustc_middle/src/middle/privacy.rs +++ b/compiler/rustc_middle/src/middle/privacy.rs @@ -28,7 +28,7 @@ pub enum AccessLevel { } /// Holds a map of accessibility levels for reachable HIR nodes. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct AccessLevels<Id = LocalDefId> { pub map: FxHashMap<Id, AccessLevel>, } diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 52ef380001c..6d1d9dd9720 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -2439,7 +2439,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { CtorKind::Fictive => { let mut struct_fmt = fmt.debug_struct(&name); for (field, place) in iter::zip(&variant_def.fields, places) { - struct_fmt.field(field.ident.as_str(), place); + struct_fmt.field(field.name.as_str(), place); } struct_fmt.finish() } @@ -2785,7 +2785,7 @@ impl UserTypeProjection { field: Field, ) -> Self { self.projs.push(ProjectionElem::Downcast( - Some(adt_def.variants[variant_index].ident.name), + Some(adt_def.variants[variant_index].name), variant_index, )); self.projs.push(ProjectionElem::Field(field, ())); diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 1422537cd50..3a6c091b331 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -247,6 +247,9 @@ pub struct CodegenUnit<'tcx> { items: FxHashMap<MonoItem<'tcx>, (Linkage, Visibility)>, size_estimate: Option<usize>, primary: bool, + /// True if this is CGU is used to hold code coverage information for dead code, + /// false otherwise. + is_code_coverage_dead_code_cgu: bool, } /// Specifies the linkage type for a `MonoItem`. @@ -277,7 +280,13 @@ pub enum Visibility { impl<'tcx> CodegenUnit<'tcx> { #[inline] pub fn new(name: Symbol) -> CodegenUnit<'tcx> { - CodegenUnit { name, items: Default::default(), size_estimate: None, primary: false } + CodegenUnit { + name, + items: Default::default(), + size_estimate: None, + primary: false, + is_code_coverage_dead_code_cgu: false, + } } pub fn name(&self) -> Symbol { @@ -304,6 +313,15 @@ impl<'tcx> CodegenUnit<'tcx> { &mut self.items } + pub fn is_code_coverage_dead_code_cgu(&self) -> bool { + self.is_code_coverage_dead_code_cgu + } + + /// Marks this CGU as the one used to contain code coverage information for dead code. + pub fn make_code_coverage_dead_code_cgu(&mut self) { + self.is_code_coverage_dead_code_cgu = true; + } + pub fn mangle_name(human_readable_name: &str) -> String { // We generate a 80 bit hash from the name. This should be enough to // avoid collisions and is still reasonably short for filenames. @@ -404,9 +422,11 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for CodegenUnit<'tcx> { // The size estimate is not relevant to the hash size_estimate: _, primary: _, + is_code_coverage_dead_code_cgu, } = *self; name.hash_stable(hcx, hasher); + is_code_coverage_dead_code_cgu.hash_stable(hcx, hasher); let mut items: Vec<(Fingerprint, _)> = items .iter() diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index b3db2e63400..3772f1c9fea 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -386,16 +386,6 @@ rustc_queries! { storage(ArenaCacheSelector<'tcx>) } - /// Returns the name of the file that contains the function body, if instrumented for coverage. - query covered_file_name(key: DefId) -> Option<Symbol> { - desc { - |tcx| "retrieving the covered file name, if instrumented, for `{}`", - tcx.def_path_str(key) - } - storage(ArenaCacheSelector<'tcx>) - cache_on_disk_if { key.is_local() } - } - /// Returns the `CodeRegions` for a function that has instrumented coverage, in case the /// function was optimized out before codegen, and before being added to the Coverage Map. query covered_code_regions(key: DefId) -> Vec<&'tcx mir::coverage::CodeRegion> { @@ -630,6 +620,32 @@ rustc_queries! { desc { |tcx| "collecting associated items of {}", tcx.def_path_str(key) } } + /// Maps from associated items on a trait to the corresponding associated + /// item on the impl specified by `impl_id`. + /// + /// For example, with the following code + /// + /// ``` + /// struct Type {} + /// // DefId + /// trait Trait { // trait_id + /// fn f(); // trait_f + /// fn g() {} // trait_g + /// } + /// + /// impl Trait for Type { // impl_id + /// fn f() {} // impl_f + /// fn g() {} // impl_g + /// } + /// ``` + /// + /// The map returned for `tcx.impl_item_implementor_ids(impl_id)` would be + ///`{ trait_f: impl_f, trait_g: impl_g }` + query impl_item_implementor_ids(impl_id: DefId) -> FxHashMap<DefId, DefId> { + desc { |tcx| "comparing impl items against trait for {}", tcx.def_path_str(impl_id) } + storage(ArenaCacheSelector<'tcx>) + } + /// Given an `impl_id`, return the trait it implements. /// Return `None` if this is an inherent impl. query impl_trait_ref(impl_id: DefId) -> Option<ty::TraitRef<'tcx>> { @@ -1274,8 +1290,8 @@ rustc_queries! { desc { "traits in scope at a block" } } - query module_exports(def_id: LocalDefId) -> Option<&'tcx [Export]> { - desc { |tcx| "looking up items exported by `{}`", tcx.def_path_str(def_id.to_def_id()) } + query module_reexports(def_id: LocalDefId) -> Option<&'tcx [ModChild]> { + desc { |tcx| "looking up reexports of module `{}`", tcx.def_path_str(def_id.to_def_id()) } } query impl_defaultness(def_id: DefId) -> hir::Defaultness { @@ -1416,13 +1432,6 @@ rustc_queries! { separate_provide_extern } - /// Given a crate, look up all trait impls in that crate. - /// Return `(impl_id, self_ty)`. - query all_trait_implementations(_: CrateNum) -> &'tcx [(DefId, Option<SimplifiedType>)] { - desc { "looking up all (?) trait implementations" } - separate_provide_extern - } - query is_dllimport_foreign_item(def_id: DefId) -> bool { desc { |tcx| "is_dllimport_foreign_item({})", tcx.def_path_str(def_id) } } @@ -1502,8 +1511,8 @@ rustc_queries! { desc { "fetching what a crate is named" } separate_provide_extern } - query item_children(def_id: DefId) -> &'tcx [Export] { - desc { |tcx| "collecting child items of `{}`", tcx.def_path_str(def_id) } + query module_children(def_id: DefId) -> &'tcx [ModChild] { + desc { |tcx| "collecting child items of module `{}`", tcx.def_path_str(def_id) } separate_provide_extern } query extern_mod_stmt_cnum(def_id: LocalDefId) -> Option<CrateNum> { @@ -1550,8 +1559,7 @@ rustc_queries! { desc { "calculating the missing lang items in a crate" } separate_provide_extern } - query visible_parent_map(_: ()) -> DefIdMap<DefId> { - storage(ArenaCacheSelector<'tcx>) + query visible_parent_map(_: ()) -> Lrc<DefIdMap<DefId>> { desc { "calculating the visible parent map" } } query trimmed_def_paths(_: ()) -> FxHashMap<DefId, Symbol> { diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 8d6fd1e729d..d2e3ce97d12 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -726,7 +726,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> { }; if let Some(variant) = variant { - write!(f, "{}", variant.ident)?; + write!(f, "{}", variant.name)?; // Only for Adt we can have `S {...}`, // which we handle separately here. @@ -738,7 +738,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> { if let PatKind::Wild = *p.pattern.kind { continue; } - let name = variant.fields[p.field.index()].ident; + let name = variant.fields[p.field.index()].name; write!(f, "{}{}: {}", start_or_comma(), name, p.pattern)?; printed += 1; } diff --git a/compiler/rustc_middle/src/traits/specialization_graph.rs b/compiler/rustc_middle/src/traits/specialization_graph.rs index 3e9cd6b46b2..087be313b26 100644 --- a/compiler/rustc_middle/src/traits/specialization_graph.rs +++ b/compiler/rustc_middle/src/traits/specialization_graph.rs @@ -4,7 +4,6 @@ use crate::ty::{self, TyCtxt}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorReported; use rustc_hir::def_id::{DefId, DefIdMap}; -use rustc_span::symbol::Ident; /// A per-trait graph of impls in specialization order. At the moment, this /// graph forms a tree rooted with the trait itself, with all other nodes @@ -75,34 +74,28 @@ pub enum Node { Trait(DefId), } -impl<'tcx> Node { +impl Node { pub fn is_from_trait(&self) -> bool { matches!(self, Node::Trait(..)) } - /// Iterate over the items defined directly by the given (impl or trait) node. - pub fn items(&self, tcx: TyCtxt<'tcx>) -> impl 'tcx + Iterator<Item = &'tcx ty::AssocItem> { - tcx.associated_items(self.def_id()).in_definition_order() - } - - /// Finds an associated item defined in this node. + /// Trys to find the associated item that implements `trait_item_def_id` + /// defined in this node. /// /// If this returns `None`, the item can potentially still be found in /// parents of this node. - pub fn item( + pub fn item<'tcx>( &self, tcx: TyCtxt<'tcx>, - trait_item_name: Ident, - trait_item_kind: ty::AssocKind, - trait_def_id: DefId, - ) -> Option<ty::AssocItem> { - tcx.associated_items(self.def_id()) - .filter_by_name_unhygienic(trait_item_name.name) - .find(move |impl_item| { - trait_item_kind == impl_item.kind - && tcx.hygienic_eq(impl_item.ident, trait_item_name, trait_def_id) - }) - .copied() + trait_item_def_id: DefId, + ) -> Option<&'tcx ty::AssocItem> { + match *self { + Node::Trait(_) => Some(tcx.associated_item(trait_item_def_id)), + Node::Impl(impl_def_id) => { + let id = tcx.impl_item_implementor_ids(impl_def_id).get(&trait_item_def_id)?; + Some(tcx.associated_item(*id)) + } + } } pub fn def_id(&self) -> DefId { @@ -181,17 +174,11 @@ impl LeafDef { impl<'tcx> Ancestors<'tcx> { /// Finds the bottom-most (ie. most specialized) definition of an associated /// item. - pub fn leaf_def( - mut self, - tcx: TyCtxt<'tcx>, - trait_item_name: Ident, - trait_item_kind: ty::AssocKind, - ) -> Option<LeafDef> { - let trait_def_id = self.trait_def_id; + pub fn leaf_def(mut self, tcx: TyCtxt<'tcx>, trait_item_def_id: DefId) -> Option<LeafDef> { let mut finalizing_node = None; self.find_map(|node| { - if let Some(item) = node.item(tcx, trait_item_name, trait_item_kind, trait_def_id) { + if let Some(item) = node.item(tcx, trait_item_def_id) { if finalizing_node.is_none() { let is_specializable = item.defaultness.is_default() || tcx.impl_defaultness(node.def_id()).is_default(); @@ -201,7 +188,7 @@ impl<'tcx> Ancestors<'tcx> { } } - Some(LeafDef { item, defining_node: node, finalizing_node }) + Some(LeafDef { item: *item, defining_node: node, finalizing_node }) } else { // Item not mentioned. This "finalizes" any defaulted item provided by an ancestor. finalizing_node = Some(node); diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 5cde54c9328..6cec75d36e2 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -4,6 +4,7 @@ use crate::ty::util::{Discr, IntTypeExt}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::stable_hasher::HashingControls; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_errors::ErrorReported; use rustc_hir as hir; @@ -136,12 +137,13 @@ impl Hash for AdtDef { impl<'a> HashStable<StableHashingContext<'a>> for AdtDef { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { thread_local! { - static CACHE: RefCell<FxHashMap<usize, Fingerprint>> = Default::default(); + static CACHE: RefCell<FxHashMap<(usize, HashingControls), Fingerprint>> = Default::default(); } let hash: Fingerprint = CACHE.with(|cache| { let addr = self as *const AdtDef as usize; - *cache.borrow_mut().entry(addr).or_insert_with(|| { + let hashing_controls = hcx.hashing_controls(); + *cache.borrow_mut().entry((addr, hashing_controls)).or_insert_with(|| { let ty::AdtDef { did, ref variants, ref flags, ref repr } = *self; let mut hasher = StableHasher::new(); diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index bf5a3e68250..5af4eef40d4 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -40,6 +40,7 @@ impl AssocItemContainer { } } +/// Information about an associated item #[derive(Copy, Clone, Debug, PartialEq, HashStable, Eq, Hash)] pub struct AssocItem { pub def_id: DefId, @@ -50,6 +51,10 @@ pub struct AssocItem { pub defaultness: hir::Defaultness, pub container: AssocItemContainer, + /// If this is an item in an impl of a trait then this is the `DefId` of + /// the associated item on the trait that this implements. + pub trait_item_def_id: Option<DefId>, + /// Whether this is a method with an explicit self /// as its first parameter, allowing method calls. pub fn_has_self_parameter: bool, diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs index 771acc29649..0ac2ea4db5e 100644 --- a/compiler/rustc_middle/src/ty/closure.rs +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -52,35 +52,18 @@ impl UpvarId { /// Information describing the capture of an upvar. This is computed /// during `typeck`, specifically by `regionck`. #[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, TypeFoldable, HashStable)] -pub enum UpvarCapture<'tcx> { +pub enum UpvarCapture { /// Upvar is captured by value. This is always true when the /// closure is labeled `move`, but can also be true in other cases /// depending on inference. - /// - /// If the upvar was inferred to be captured by value (e.g. `move` - /// was not used), then the `Span` points to a usage that - /// required it. There may be more than one such usage - /// (e.g. `|| { a; a; }`), in which case we pick an - /// arbitrary one. - ByValue(Option<Span>), + ByValue, /// Upvar is captured by reference. - ByRef(UpvarBorrow<'tcx>), -} - -#[derive(PartialEq, Clone, Copy, TyEncodable, TyDecodable, TypeFoldable, HashStable)] -pub struct UpvarBorrow<'tcx> { - /// The kind of borrow: by-ref upvars have access to shared - /// immutable borrows, which are not part of the normal language - /// syntax. - pub kind: BorrowKind, - - /// Region of the resulting reference. - pub region: ty::Region<'tcx>, + ByRef(BorrowKind), } pub type UpvarListMap = FxHashMap<DefId, FxIndexMap<hir::HirId, UpvarId>>; -pub type UpvarCaptureMap<'tcx> = FxHashMap<UpvarId, UpvarCapture<'tcx>>; +pub type UpvarCaptureMap = FxHashMap<UpvarId, UpvarCapture>; /// Given the closure DefId this map provides a map of root variables to minimum /// set of `CapturedPlace`s that need to be tracked to support all captures of that closure. @@ -150,10 +133,13 @@ pub struct CapturedPlace<'tcx> { pub place: HirPlace<'tcx>, /// `CaptureKind` and expression(s) that resulted in such capture of `place`. - pub info: CaptureInfo<'tcx>, + pub info: CaptureInfo, /// Represents if `place` can be mutated or not. pub mutability: hir::Mutability, + + /// Region of the resulting reference if the upvar is captured by ref. + pub region: Option<ty::Region<'tcx>>, } impl<'tcx> CapturedPlace<'tcx> { @@ -178,7 +164,7 @@ impl<'tcx> CapturedPlace<'tcx> { write!( &mut symbol, "__{}", - def.variants[variant].fields[idx as usize].ident.name.as_str(), + def.variants[variant].fields[idx as usize].name.as_str(), ) .unwrap(); } @@ -287,7 +273,7 @@ pub fn is_ancestor_or_same_capture( /// for a particular capture as well as identifying the part of the source code /// that triggered this capture to occur. #[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, TypeFoldable, HashStable)] -pub struct CaptureInfo<'tcx> { +pub struct CaptureInfo { /// Expr Id pointing to use that resulted in selecting the current capture kind /// /// Eg: @@ -325,7 +311,7 @@ pub struct CaptureInfo<'tcx> { pub path_expr_id: Option<hir::HirId>, /// Capture mode that was selected - pub capture_kind: UpvarCapture<'tcx>, + pub capture_kind: UpvarCapture, } pub fn place_to_string_for_capture<'tcx>(tcx: TyCtxt<'tcx>, place: &HirPlace<'tcx>) -> String { @@ -344,7 +330,7 @@ pub fn place_to_string_for_capture<'tcx>(tcx: TyCtxt<'tcx>, place: &HirPlace<'tc curr_string = format!( "{}.{}", curr_string, - def.variants[variant].fields[idx as usize].ident.name.as_str() + def.variants[variant].fields[idx as usize].name.as_str() ); } ty::Tuple(_) => { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index dd571e29bf6..ecc6da6da12 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1661,7 +1661,7 @@ CloneLiftImpls! { for<'tcx> { Constness, traits::WellFormedLoc, } } pub mod tls { use super::{ptr_eq, GlobalCtxt, TyCtxt}; - use crate::dep_graph::{DepKind, TaskDeps}; + use crate::dep_graph::{DepKind, TaskDepsRef}; use crate::ty::query; use rustc_data_structures::sync::{self, Lock}; use rustc_data_structures::thin_vec::ThinVec; @@ -1697,13 +1697,19 @@ pub mod tls { /// The current dep graph task. This is used to add dependencies to queries /// when executing them. - pub task_deps: Option<&'a Lock<TaskDeps>>, + pub task_deps: TaskDepsRef<'a>, } impl<'a, 'tcx> ImplicitCtxt<'a, 'tcx> { pub fn new(gcx: &'tcx GlobalCtxt<'tcx>) -> Self { let tcx = TyCtxt { gcx }; - ImplicitCtxt { tcx, query: None, diagnostics: None, layout_depth: 0, task_deps: None } + ImplicitCtxt { + tcx, + query: None, + diagnostics: None, + layout_depth: 0, + task_deps: TaskDepsRef::Ignore, + } } } @@ -2452,7 +2458,7 @@ impl<'tcx> TyCtxt<'tcx> { ) -> Place<'tcx> { self.mk_place_elem( place, - PlaceElem::Downcast(Some(adt_def.variants[variant_index].ident.name), variant_index), + PlaceElem::Downcast(Some(adt_def.variants[variant_index].name), variant_index), ) } @@ -2820,7 +2826,8 @@ pub fn provide(providers: &mut ty::query::Providers) { providers.in_scope_traits_map = |tcx, id| tcx.hir_crate(()).owners[id].as_ref().map(|owner_info| &owner_info.trait_map); providers.resolutions = |tcx, ()| &tcx.untracked_resolutions; - providers.module_exports = |tcx, id| tcx.resolutions(()).export_map.get(&id).map(|v| &v[..]); + providers.module_reexports = + |tcx, id| tcx.resolutions(()).reexport_map.get(&id).map(|v| &v[..]); providers.crate_name = |tcx, id| { assert_eq!(id, LOCAL_CRATE); tcx.crate_name diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index 1c3a01e2cfa..0bd96f8f865 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -31,6 +31,13 @@ impl GenericParamDefKind { GenericParamDefKind::Const { .. } => ast::ParamKindOrd::Const, } } + + pub fn is_ty_or_const(&self) -> bool { + match self { + GenericParamDefKind::Lifetime => false, + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => true, + } + } } #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] diff --git a/compiler/rustc_middle/src/ty/impls_ty.rs b/compiler/rustc_middle/src/ty/impls_ty.rs index 9f47ed89f13..00ce15bea3f 100644 --- a/compiler/rustc_middle/src/ty/impls_ty.rs +++ b/compiler/rustc_middle/src/ty/impls_ty.rs @@ -6,6 +6,7 @@ use crate::mir; use crate::ty; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::stable_hasher::HashingControls; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; use rustc_query_system::ich::StableHashingContext; use std::cell::RefCell; @@ -17,12 +18,12 @@ where { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { thread_local! { - static CACHE: RefCell<FxHashMap<(usize, usize), Fingerprint>> = + static CACHE: RefCell<FxHashMap<(usize, usize, HashingControls), Fingerprint>> = RefCell::new(Default::default()); } let hash = CACHE.with(|cache| { - let key = (self.as_ptr() as usize, self.len()); + let key = (self.as_ptr() as usize, self.len(), hcx.hashing_controls()); if let Some(&hash) = cache.borrow().get(&key) { return hash; } diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 196fe7ce1b6..4e6b2acb67f 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -10,7 +10,7 @@ use rustc_hir::lang_items::LangItem; use rustc_index::bit_set::BitSet; use rustc_index::vec::{Idx, IndexVec}; use rustc_session::{config::OptLevel, DataTypeKind, FieldInfo, SizeKind, VariantInfo}; -use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::symbol::Symbol; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::call::{ ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, Conv, FnAbi, PassMode, Reg, RegKind, @@ -1810,7 +1810,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { let adt_kind = adt_def.adt_kind(); let adt_packed = adt_def.repr.pack.is_some(); - let build_variant_info = |n: Option<Ident>, flds: &[Symbol], layout: TyAndLayout<'tcx>| { + let build_variant_info = |n: Option<Symbol>, flds: &[Symbol], layout: TyAndLayout<'tcx>| { let mut min_size = Size::ZERO; let field_info: Vec<_> = flds .iter() @@ -1845,15 +1845,15 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { if !adt_def.variants.is_empty() && layout.fields != FieldsShape::Primitive { debug!( "print-type-size `{:#?}` variant {}", - layout, adt_def.variants[index].ident + layout, adt_def.variants[index].name ); let variant_def = &adt_def.variants[index]; - let fields: Vec<_> = variant_def.fields.iter().map(|f| f.ident.name).collect(); + let fields: Vec<_> = variant_def.fields.iter().map(|f| f.name).collect(); record( adt_kind.into(), adt_packed, None, - vec![build_variant_info(Some(variant_def.ident), &fields, layout)], + vec![build_variant_info(Some(variant_def.name), &fields, layout)], ); } else { // (This case arises for *empty* enums; so give it @@ -1872,10 +1872,9 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { .variants .iter_enumerated() .map(|(i, variant_def)| { - let fields: Vec<_> = - variant_def.fields.iter().map(|f| f.ident.name).collect(); + let fields: Vec<_> = variant_def.fields.iter().map(|f| f.name).collect(); build_variant_info( - Some(variant_def.ident), + Some(variant_def.name), &fields, layout.for_variant(self, i), ) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 78ccfbd5e8c..f1868459f27 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -19,7 +19,8 @@ pub use assoc::*; pub use generics::*; pub use vtable::*; -use crate::hir::exports::ExportMap; +use crate::metadata::ModChild; +use crate::middle::privacy::AccessLevels; use crate::mir::{Body, GeneratorLayout}; use crate::traits::{self, Reveal}; use crate::ty; @@ -56,8 +57,8 @@ pub use self::binding::BindingMode::*; pub use self::closure::{ is_ancestor_or_same_capture, place_to_string_for_capture, BorrowKind, CaptureInfo, CapturedPlace, ClosureKind, MinCaptureInformationMap, MinCaptureList, - RootVariableMinCaptureList, UpvarBorrow, UpvarCapture, UpvarCaptureMap, UpvarId, UpvarListMap, - UpvarPath, CAPTURE_STRUCT_LOCAL, + RootVariableMinCaptureList, UpvarCapture, UpvarCaptureMap, UpvarId, UpvarListMap, UpvarPath, + CAPTURE_STRUCT_LOCAL, }; pub use self::consts::{Const, ConstInt, ConstKind, InferConst, ScalarInt, Unevaluated, ValTree}; pub use self::context::{ @@ -123,10 +124,11 @@ pub struct ResolverOutputs { pub definitions: rustc_hir::definitions::Definitions, pub cstore: Box<CrateStoreDyn>, pub visibilities: FxHashMap<LocalDefId, Visibility>, + pub access_levels: AccessLevels, pub extern_crate_map: FxHashMap<LocalDefId, CrateNum>, pub maybe_unused_trait_imports: FxHashSet<LocalDefId>, pub maybe_unused_extern_crates: Vec<(LocalDefId, Span)>, - pub export_map: ExportMap, + pub reexport_map: FxHashMap<LocalDefId, Vec<ModChild>>, pub glob_map: FxHashMap<LocalDefId, FxHashSet<Symbol>>, /// Extern prelude entries. The value is `true` if the entry was introduced /// via `extern crate` item and not `--extern` option or compiler built-in. @@ -1502,8 +1504,7 @@ pub struct VariantDef { /// If this variant is a struct variant, then this is `None`. pub ctor_def_id: Option<DefId>, /// Variant or struct name. - #[stable_hasher(project(name))] - pub ident: Ident, + pub name: Symbol, /// Discriminant of this variant. pub discr: VariantDiscr, /// Fields of this variant. @@ -1532,7 +1533,7 @@ impl VariantDef { /// If someone speeds up attribute loading to not be a performance concern, they can /// remove this hack and use the constructor `DefId` everywhere. pub fn new( - ident: Ident, + name: Symbol, variant_did: Option<DefId>, ctor_def_id: Option<DefId>, discr: VariantDiscr, @@ -1544,9 +1545,9 @@ impl VariantDef { is_field_list_non_exhaustive: bool, ) -> Self { debug!( - "VariantDef::new(ident = {:?}, variant_did = {:?}, ctor_def_id = {:?}, discr = {:?}, + "VariantDef::new(name = {:?}, variant_did = {:?}, ctor_def_id = {:?}, discr = {:?}, fields = {:?}, ctor_kind = {:?}, adt_kind = {:?}, parent_did = {:?})", - ident, variant_did, ctor_def_id, discr, fields, ctor_kind, adt_kind, parent_did, + name, variant_did, ctor_def_id, discr, fields, ctor_kind, adt_kind, parent_did, ); let mut flags = VariantFlags::NO_VARIANT_FLAGS; @@ -1561,7 +1562,7 @@ impl VariantDef { VariantDef { def_id: variant_did.unwrap_or(parent_did), ctor_def_id, - ident, + name, discr, fields, ctor_kind, @@ -1580,6 +1581,11 @@ impl VariantDef { pub fn is_recovered(&self) -> bool { self.flags.intersects(VariantFlags::IS_RECOVERED) } + + /// Computes the `Ident` of this variant by looking up the `Span` + pub fn ident(&self, tcx: TyCtxt<'_>) -> Ident { + Ident::new(self.name, tcx.def_ident_span(self.def_id).unwrap()) + } } #[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)] @@ -1598,8 +1604,7 @@ pub enum VariantDiscr { #[derive(Debug, HashStable, TyEncodable, TyDecodable)] pub struct FieldDef { pub did: DefId, - #[stable_hasher(project(name))] - pub ident: Ident, + pub name: Symbol, pub vis: Visibility, } @@ -1774,6 +1779,11 @@ impl<'tcx> FieldDef { pub fn ty(&self, tcx: TyCtxt<'tcx>, subst: SubstsRef<'tcx>) -> Ty<'tcx> { tcx.type_of(self.did).subst(tcx, subst) } + + /// Computes the `Ident` of this variant by looking up the `Span` + pub fn ident(&self, tcx: TyCtxt<'_>) -> Ident { + Ident::new(self.name, tcx.def_ident_span(self.did).unwrap()) + } } pub type Attributes<'tcx> = &'tcx [ast::Attribute]; @@ -1890,7 +1900,10 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn find_field_index(self, ident: Ident, variant: &VariantDef) -> Option<usize> { - variant.fields.iter().position(|field| self.hygienic_eq(ident, field.ident, variant.def_id)) + variant + .fields + .iter() + .position(|field| self.hygienic_eq(ident, field.ident(self), variant.def_id)) } /// Returns `true` if the impls are the same polarity and the trait either diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index 94127a144df..7b5905fddc9 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -188,6 +188,11 @@ pub trait Printer<'tcx>: Sized { own_params.start = 1; } + // If we're in verbose mode, then print default-equal args too + if self.tcx().sess.verbose() { + return &substs[own_params]; + } + // Don't print args that are the defaults of their respective parameters. own_params.end -= generics .params diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 47a9234419c..b2ae6e6fae6 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -458,7 +458,7 @@ pub trait PrettyPrinter<'tcx>: // that's public and whose identifier isn't `_`. let reexport = self .tcx() - .item_children(visible_parent) + .module_children(visible_parent) .iter() .filter(|child| child.res.opt_def_id() == Some(def_id)) .find(|child| child.vis.is_public() && child.ident.name != kw::Underscore) @@ -1475,7 +1475,7 @@ pub trait PrettyPrinter<'tcx>: if !first { p!(", "); } - p!(write("{}: ", field_def.ident), print(field)); + p!(write("{}: ", field_def.name), print(field)); first = false; } p!(" }}"); @@ -1784,10 +1784,11 @@ impl<'tcx, F: fmt::Write> Printer<'tcx> for FmtPrinter<'_, 'tcx, F> { self = print_prefix(self)?; // Don't print `'_` if there's no unerased regions. - let print_regions = args.iter().any(|arg| match arg.unpack() { - GenericArgKind::Lifetime(r) => *r != ty::ReErased, - _ => false, - }); + let print_regions = self.tcx.sess.verbose() + || args.iter().any(|arg| match arg.unpack() { + GenericArgKind::Lifetime(r) => *r != ty::ReErased, + _ => false, + }); let args = args.iter().cloned().filter(|arg| match arg.unpack() { GenericArgKind::Lifetime(_) => print_regions, _ => true, @@ -2602,7 +2603,7 @@ fn for_each_def(tcx: TyCtxt<'_>, mut collect_fn: impl for<'b> FnMut(&'b Ident, N // Iterate external crate defs but be mindful about visibility while let Some(def) = queue.pop() { - for child in tcx.item_children(def).iter() { + for child in tcx.module_children(def).iter() { if !child.vis.is_public() { continue; } @@ -2615,7 +2616,9 @@ fn for_each_def(tcx: TyCtxt<'_>, mut collect_fn: impl for<'b> FnMut(&'b Ident, N collect_fn(&child.ident, ns, def_id); } - if seen_defs.insert(def_id) { + if matches!(defkind, DefKind::Mod | DefKind::Enum | DefKind::Trait) + && seen_defs.insert(def_id) + { queue.push(def_id); } } diff --git a/compiler/rustc_middle/src/ty/query.rs b/compiler/rustc_middle/src/ty/query.rs index 3af1b3a0440..4a38d1c422f 100644 --- a/compiler/rustc_middle/src/ty/query.rs +++ b/compiler/rustc_middle/src/ty/query.rs @@ -1,7 +1,7 @@ use crate::dep_graph; -use crate::hir::exports::Export; use crate::infer::canonical::{self, Canonical}; use crate::lint::LintLevelMap; +use crate::metadata::ModChild; use crate::middle::codegen_fn_attrs::CodegenFnAttrs; use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel}; use crate::middle::lib_features::LibFeatures; diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 98b1a8b4d76..bb8c2b180e4 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -47,12 +47,6 @@ impl fmt::Debug for ty::UpvarId { } } -impl<'tcx> fmt::Debug for ty::UpvarBorrow<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "UpvarBorrow({:?}, {:?})", self.kind, self.region) - } -} - impl<'tcx> fmt::Debug for ty::ExistentialTraitRef<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { with_no_trimmed_paths(|| fmt::Display::fmt(self, f)) diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index c24a1d8eb52..0d37711d72e 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -2143,9 +2143,12 @@ impl<'tcx> TyS<'tcx> { } /// Returns the type of metadata for (potentially fat) pointers to this type. - pub fn ptr_metadata_ty(&'tcx self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - // FIXME: should this normalize? - let tail = tcx.struct_tail_without_normalization(self); + pub fn ptr_metadata_ty( + &'tcx self, + tcx: TyCtxt<'tcx>, + normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>, + ) -> Ty<'tcx> { + let tail = tcx.struct_tail_with_normalize(self, normalize); match tail.kind() { // Sized types ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 669065598f1..8793264a47f 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -192,7 +192,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn struct_tail_with_normalize( self, mut ty: Ty<'tcx>, - normalize: impl Fn(Ty<'tcx>) -> Ty<'tcx>, + mut normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>, ) -> Ty<'tcx> { let recursion_limit = self.recursion_limit(); for iteration in 0.. { diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index c6a34ece245..3c07077c2cb 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -266,7 +266,7 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>( // we need to deref it upvar_resolved_place_builder = match capture.info.capture_kind { ty::UpvarCapture::ByRef(_) => upvar_resolved_place_builder.deref(), - ty::UpvarCapture::ByValue(_) => upvar_resolved_place_builder, + ty::UpvarCapture::ByValue => upvar_resolved_place_builder, }; let next_projection = capture.place.projections.len(); @@ -336,10 +336,7 @@ impl<'tcx> PlaceBuilder<'tcx> { } crate fn downcast(self, adt_def: &'tcx AdtDef, variant_index: VariantIdx) -> Self { - self.project(PlaceElem::Downcast( - Some(adt_def.variants[variant_index].ident.name), - variant_index, - )) + self.project(PlaceElem::Downcast(Some(adt_def.variants[variant_index].name), variant_index)) } fn index(self, index: Local) -> Self { diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index a01df2372a0..7ed5d1d67ab 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -754,10 +754,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // So, if we have a match-pattern like `x @ Enum::Variant(P1, P2)`, // we want to create a set of derived match-patterns like // `(x as Variant).0 @ P1` and `(x as Variant).1 @ P1`. - let elem = ProjectionElem::Downcast( - Some(adt_def.variants[variant_index].ident.name), - variant_index, - ); + let elem = + ProjectionElem::Downcast(Some(adt_def.variants[variant_index].name), variant_index); let downcast_place = match_pair.place.project(elem); // `(x as Variant)` let consequent_match_pairs = subpatterns.iter().map(|subpattern| { // e.g., `(x as Variant).0` diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index cb94e759972..a4f3a63e40b 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -930,7 +930,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let mut projs = closure_env_projs.clone(); projs.push(ProjectionElem::Field(Field::new(i), ty)); match capture { - ty::UpvarCapture::ByValue(_) => {} + ty::UpvarCapture::ByValue => {} ty::UpvarCapture::ByRef(..) => { projs.push(ProjectionElem::Deref); } diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index bdde6b4a356..c62de154388 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -1108,9 +1108,9 @@ impl<'tcx> Cx<'tcx> { let temp_lifetime = self.region_scope_tree.temporary_scope(closure_expr.hir_id.local_id); match upvar_capture { - ty::UpvarCapture::ByValue(_) => captured_place_expr, + ty::UpvarCapture::ByValue => captured_place_expr, ty::UpvarCapture::ByRef(upvar_borrow) => { - let borrow_kind = match upvar_borrow.kind { + let borrow_kind = match upvar_borrow { ty::BorrowKind::ImmBorrow => BorrowKind::Shared, ty::BorrowKind::UniqueImmBorrow => BorrowKind::Unique, ty::BorrowKind::MutBorrow => BorrowKind::Mut { allow_two_phase_borrow: false }, 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 7a4fd6ffc4a..0980c669f33 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -327,7 +327,7 @@ fn check_for_bindings_named_same_as_variants( if let ty::Adt(edef, _) = pat_ty.kind() { if edef.is_enum() && edef.variants.iter().any(|variant| { - variant.ident == ident && variant.ctor_kind == CtorKind::Const + variant.ident(cx.tcx) == ident && variant.ctor_kind == CtorKind::Const }) { let variant_count = edef.variants.len(); @@ -627,7 +627,7 @@ fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>( continue; } } - let sp = def.variants[*variant_index].ident.span; + let sp = def.variants[*variant_index].ident(cx.tcx).span; if covered.contains(&sp) { // Don't point at variants that have already been covered due to other patterns to avoid // visual clutter. diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index 368e3957dd0..801c8778bff 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -1648,7 +1648,7 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> { }; if let Some(variant) = variant { - write!(f, "{}", variant.ident)?; + write!(f, "{}", variant.name)?; } // Without `cx`, we can't know which field corresponds to which, so we can't diff --git a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs index 11856f6e047..501bc96401a 100644 --- a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs +++ b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs @@ -491,7 +491,7 @@ where if let Some(variant_path) = subpath { let base_place = tcx.mk_place_elem( self.place, - ProjectionElem::Downcast(Some(variant.ident.name), variant_index), + ProjectionElem::Downcast(Some(variant.name), variant_index), ); let fields = self.move_paths_for_fields(base_place, variant_path, &variant, substs); values.push(discr.val); diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index 1721fb5cde0..46de6d939a1 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -9,7 +9,6 @@ use rustc_span::def_id::DefId; /// A `query` provider for retrieving coverage information injected into MIR. pub(crate) fn provide(providers: &mut Providers) { providers.coverageinfo = |tcx, def_id| coverageinfo(tcx, def_id); - providers.covered_file_name = |tcx, def_id| covered_file_name(tcx, def_id); providers.covered_code_regions = |tcx, def_id| covered_code_regions(tcx, def_id); } @@ -137,25 +136,6 @@ fn coverageinfo<'tcx>(tcx: TyCtxt<'tcx>, instance_def: ty::InstanceDef<'tcx>) -> coverage_visitor.info } -fn covered_file_name(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Symbol> { - if tcx.is_mir_available(def_id) { - let body = mir_body(tcx, def_id); - for bb_data in body.basic_blocks().iter() { - for statement in bb_data.statements.iter() { - if let StatementKind::Coverage(box ref coverage) = statement.kind { - if let Some(code_region) = coverage.code_region.as_ref() { - if is_inlined(body, statement) { - continue; - } - return Some(code_region.file_name); - } - } - } - } - } - return None; -} - fn covered_code_regions<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Vec<&'tcx CodeRegion> { let body = mir_body(tcx, def_id); body.basic_blocks() diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs index bc9a104e849..08247e6f22a 100644 --- a/compiler/rustc_mir_transform/src/generator.rs +++ b/compiler/rustc_mir_transform/src/generator.rs @@ -726,9 +726,13 @@ fn sanitize_witness<'tcx>( saved_locals: &GeneratorSavedLocals, ) { let did = body.source.def_id(); - let allowed_upvars = tcx.erase_regions(upvars); + let param_env = tcx.param_env(did); + + let allowed_upvars = tcx.normalize_erasing_regions(param_env, upvars); let allowed = match witness.kind() { - &ty::GeneratorWitness(s) => tcx.erase_late_bound_regions(s), + &ty::GeneratorWitness(interior_tys) => { + tcx.normalize_erasing_late_bound_regions(param_env, interior_tys) + } _ => { tcx.sess.delay_span_bug( body.span, @@ -738,8 +742,6 @@ fn sanitize_witness<'tcx>( } }; - let param_env = tcx.param_env(did); - for (local, decl) in body.local_decls.iter_enumerated() { // Ignore locals which are internal or not saved between yields. if !saved_locals.contains(local) || decl.internal { diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index b70c24b76d5..3e06e7f36d4 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1310,10 +1310,9 @@ fn create_mono_items_for_default_impls<'tcx>( if let Some(trait_ref) = tcx.impl_trait_ref(item.def_id) { let param_env = ty::ParamEnv::reveal_all(); let trait_ref = tcx.normalize_erasing_regions(param_env, trait_ref); - let overridden_methods: FxHashSet<_> = - impl_.items.iter().map(|iiref| iiref.ident.normalize_to_macros_2_0()).collect(); + let overridden_methods = tcx.impl_item_implementor_ids(item.def_id); for method in tcx.provided_trait_methods(trait_ref.def_id) { - if overridden_methods.contains(&method.ident.normalize_to_macros_2_0()) { + if overridden_methods.contains_key(&method.def_id) { continue; } diff --git a/compiler/rustc_monomorphize/src/partitioning/mod.rs b/compiler/rustc_monomorphize/src/partitioning/mod.rs index dc22ffc6747..67597a0d7b4 100644 --- a/compiler/rustc_monomorphize/src/partitioning/mod.rs +++ b/compiler/rustc_monomorphize/src/partitioning/mod.rs @@ -201,6 +201,40 @@ pub fn partition<'tcx>( partitioner.internalize_symbols(cx, &mut post_inlining); } + let instrument_dead_code = + tcx.sess.instrument_coverage() && !tcx.sess.instrument_coverage_except_unused_functions(); + + if instrument_dead_code { + assert!( + post_inlining.codegen_units.len() > 0, + "There must be at least one CGU that code coverage data can be generated in." + ); + + // Find the smallest CGU that has exported symbols and put the dead + // function stubs in that CGU. We look for exported symbols to increase + // the likelihood the linker won't throw away the dead functions. + // FIXME(#92165): In order to truly resolve this, we need to make sure + // the object file (CGU) containing the dead function stubs is included + // in the final binary. This will probably require forcing these + // function symbols to be included via `-u` or `/include` linker args. + let mut cgus: Vec<_> = post_inlining.codegen_units.iter_mut().collect(); + cgus.sort_by_key(|cgu| cgu.size_estimate()); + + let dead_code_cgu = if let Some(cgu) = cgus + .into_iter() + .rev() + .filter(|cgu| cgu.items().iter().any(|(_, (linkage, _))| *linkage == Linkage::External)) + .next() + { + cgu + } else { + // If there are no CGUs that have externally linked items, + // then we just pick the first CGU as a fallback. + &mut post_inlining.codegen_units[0] + }; + dead_code_cgu.make_code_coverage_dead_code_cgu(); + } + // Finally, sort by codegen unit name, so that we get deterministic results. let PostInliningPartitioning { codegen_units: mut result, diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 9677e7642b8..4121a759c37 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -550,8 +550,8 @@ impl<'a> Parser<'a> { /// a diagnostic to suggest removing them. /// /// ```ignore (diagnostic) - /// let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>>>(); - /// ^^ help: remove extra angle brackets + /// let _ = [1, 2, 3].into_iter().collect::<Vec<usize>>>>(); + /// ^^ help: remove extra angle brackets /// ``` /// /// If `true` is returned, then trailing brackets were recovered, tokens were consumed diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index a5a65740707..b755f686f6a 100644 --- a/compiler/rustc_passes/src/check_const.rs +++ b/compiler/rustc_passes/src/check_const.rs @@ -93,26 +93,29 @@ impl<'tcx> hir::itemlikevisit::ItemLikeVisitor<'tcx> for CheckConstTraitVisitor< for trait_item in self.tcx.associated_items(trait_def_id).in_definition_order() { if let ty::AssocItem { - kind: ty::AssocKind::Fn, ident, defaultness, .. - } = trait_item + kind: ty::AssocKind::Fn, + defaultness, + def_id: trait_item_id, + .. + } = *trait_item { // we can ignore functions that do not have default bodies: // if those are unimplemented it will be catched by typeck. if !defaultness.has_value() || self .tcx - .has_attr(trait_item.def_id, sym::default_method_body_is_const) + .has_attr(trait_item_id, sym::default_method_body_is_const) { continue; } let is_implemented = ancestors - .leaf_def(self.tcx, trait_item.ident, trait_item.kind) + .leaf_def(self.tcx, trait_item_id) .map(|node_item| !node_item.defining_node.is_from_trait()) .unwrap_or(false); if !is_implemented { - to_implement.push(ident.to_string()); + to_implement.push(self.tcx.item_name(trait_item_id).to_string()); } } } diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs index a808d6c8348..0c934ecc913 100644 --- a/compiler/rustc_passes/src/lang_items.rs +++ b/compiler/rustc_passes/src/lang_items.rs @@ -10,7 +10,6 @@ use crate::check_attr::target_from_impl_item; use crate::weak_lang_items; -use rustc_ast::Attribute; use rustc_errors::{pluralize, struct_span_err}; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -57,8 +56,7 @@ impl<'tcx> LanguageItemCollector<'tcx> { fn check_for_lang(&mut self, actual_target: Target, hir_id: HirId) { let attrs = self.tcx.hir().attrs(hir_id); - let check_name = |attr: &Attribute, sym| attr.has_name(sym); - if let Some((value, span)) = extract(check_name, &attrs) { + if let Some((value, span)) = extract(&attrs) { match ITEM_REFS.get(&value).cloned() { // Known lang item with attribute on correct target. Some((item_index, expected_target)) if actual_target == expected_target => { diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 9ee305b712f..2e3bf7ea548 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -726,7 +726,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { ); self.acc(self.exit_ln, var, ACC_READ | ACC_USE); } - ty::UpvarCapture::ByValue(_) => {} + ty::UpvarCapture::ByValue => {} } } } @@ -1481,7 +1481,7 @@ impl<'tcx> Liveness<'_, 'tcx> { for (&var_hir_id, min_capture_list) in closure_min_captures { for captured_place in min_capture_list { match captured_place.info.capture_kind { - ty::UpvarCapture::ByValue(_) => {} + ty::UpvarCapture::ByValue => {} ty::UpvarCapture::ByRef(..) => continue, }; let span = captured_place.get_capture_kind_span(self.ir.tcx); diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 5f19991f9c7..c136411df27 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -794,19 +794,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { } } - if let Res::Def(DefKind::Trait, trait_did) = t.path.res { - for impl_item_ref in items { - let impl_item = self.tcx.hir().impl_item(impl_item_ref.id); - let trait_item_def_id = self - .tcx - .associated_items(trait_did) - .filter_by_name_unhygienic(impl_item.ident.name) - .next() - .map(|item| item.def_id); - if let Some(def_id) = trait_item_def_id { - // Pass `None` to skip deprecation warnings. - self.tcx.check_stability(def_id, None, impl_item.span, None); - } + for impl_item_ref in items { + let impl_item = self.tcx.associated_item(impl_item_ref.id.def_id); + + if let Some(def_id) = impl_item.trait_item_def_id { + // Pass `None` to skip deprecation warnings. + self.tcx.check_stability(def_id, None, impl_item_ref.span, None); } } } diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs index 61c82f031dd..21514d19f6a 100644 --- a/compiler/rustc_passes/src/weak_lang_items.rs +++ b/compiler/rustc_passes/src/weak_lang_items.rs @@ -1,6 +1,5 @@ //! Validity checking for weak lang items -use rustc_ast::Attribute; use rustc_data_structures::fx::FxHashSet; use rustc_errors::struct_span_err; use rustc_hir as hir; @@ -103,9 +102,8 @@ impl<'a, 'tcx, 'v> Visitor<'v> for Context<'a, 'tcx> { } fn visit_foreign_item(&mut self, i: &hir::ForeignItem<'_>) { - let check_name = |attr: &Attribute, sym| attr.has_name(sym); let attrs = self.tcx.hir().attrs(i.hir_id()); - if let Some((lang_item, _)) = lang_items::extract(check_name, attrs) { + if let Some((lang_item, _)) = lang_items::extract(attrs) { self.register(lang_item, i.span); } intravisit::walk_foreign_item(self, i) diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 183a5a205ec..e7741ccc4e4 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -11,8 +11,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet}; -use rustc_hir::def_id::{CRATE_DEF_ID, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet, CRATE_DEF_ID}; use rustc_hir::intravisit::{self, DeepVisitor, NestedVisitorMap, Visitor}; use rustc_hir::{AssocItemKind, HirIdSet, Node, PatKind}; use rustc_middle::bug; @@ -26,7 +25,7 @@ use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::{self, Const, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeFoldable}; use rustc_session::lint; use rustc_span::hygiene::Transparency; -use rustc_span::symbol::{kw, sym, Ident}; +use rustc_span::symbol::{kw, Ident}; use rustc_span::Span; use rustc_trait_selection::traits::const_evaluatable::{self, AbstractConst}; @@ -436,6 +435,15 @@ impl<'tcx> EmbargoVisitor<'tcx> { self.access_levels.map.get(&def_id).copied() } + fn update_with_hir_id( + &mut self, + hir_id: hir::HirId, + level: Option<AccessLevel>, + ) -> Option<AccessLevel> { + let def_id = self.tcx.hir().local_def_id(hir_id); + self.update(def_id, level) + } + /// Updates node level and returns the updated level. fn update(&mut self, def_id: LocalDefId, level: Option<AccessLevel>) -> Option<AccessLevel> { let old_level = self.get(def_id); @@ -520,7 +528,7 @@ impl<'tcx> EmbargoVisitor<'tcx> { let vis = self.tcx.visibility(item_id.def_id); self.update_macro_reachable_def(item_id.def_id, def_kind, vis, defining_mod); } - if let Some(exports) = self.tcx.module_exports(module_def_id) { + if let Some(exports) = self.tcx.module_reexports(module_def_id) { for export in exports { if export.vis.is_accessible_from(defining_mod.to_def_id(), self.tcx) { if let Res::Def(def_kind, def_id) = export.res { @@ -623,54 +631,6 @@ impl<'tcx> EmbargoVisitor<'tcx> { | DefKind::Generator => (), } } - - /// Given the path segments of an `ItemKind::Use`, then we need - /// to update the visibility of the intermediate use so that it isn't linted - /// by `unreachable_pub`. - /// - /// This isn't trivial as `path.res` has the `DefId` of the eventual target - /// of the use statement not of the next intermediate use statement. - /// - /// To do this, consider the last two segments of the path to our intermediate - /// use statement. We expect the penultimate segment to be a module and the - /// last segment to be the name of the item we are exporting. We can then - /// look at the items contained in the module for the use statement with that - /// name and update that item's visibility. - /// - /// FIXME: This solution won't work with glob imports and doesn't respect - /// namespaces. See <https://github.com/rust-lang/rust/pull/57922#discussion_r251234202>. - fn update_visibility_of_intermediate_use_statements( - &mut self, - segments: &[hir::PathSegment<'_>], - ) { - if let [.., module, segment] = segments { - if let Some(item) = module - .res - .and_then(|res| res.mod_def_id()) - // If the module is `self`, i.e. the current crate, - // there will be no corresponding item. - .filter(|def_id| def_id.index != CRATE_DEF_INDEX || def_id.krate != LOCAL_CRATE) - .and_then(|def_id| def_id.as_local()) - .map(|module_hir_id| self.tcx.hir().expect_item(module_hir_id)) - { - if let hir::ItemKind::Mod(m) = &item.kind { - for &item_id in m.item_ids { - let item = self.tcx.hir().item(item_id); - if !self.tcx.hygienic_eq( - segment.ident, - item.ident, - item_id.def_id.to_def_id(), - ) { - continue; - } - if let hir::ItemKind::Use(..) = item.kind { - self.update(item.def_id, Some(AccessLevel::Exported)); - } - } - } - } - } - } } impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { @@ -683,56 +643,25 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { } fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { - let inherited_item_level = match item.kind { + let item_level = match item.kind { hir::ItemKind::Impl { .. } => { - Option::<AccessLevel>::of_impl(item.def_id, self.tcx, &self.access_levels) - } - // Only exported `macro_rules!` items are public, but they always are. - hir::ItemKind::Macro(MacroDef { macro_rules: true, .. }) => { - let def_id = item.def_id.to_def_id(); - let is_macro_export = self.tcx.has_attr(def_id, sym::macro_export); - if is_macro_export { Some(AccessLevel::Public) } else { None } - } - // Foreign modules inherit level from parents. - hir::ItemKind::ForeignMod { .. } => self.prev_level, - // Other `pub` items inherit levels from parents. - hir::ItemKind::Const(..) - | hir::ItemKind::Enum(..) - | hir::ItemKind::ExternCrate(..) - | hir::ItemKind::GlobalAsm(..) - | hir::ItemKind::Fn(..) - | hir::ItemKind::Macro(..) - | hir::ItemKind::Mod(..) - | hir::ItemKind::Static(..) - | hir::ItemKind::Struct(..) - | hir::ItemKind::Trait(..) - | hir::ItemKind::TraitAlias(..) - | hir::ItemKind::OpaqueTy(..) - | hir::ItemKind::TyAlias(..) - | hir::ItemKind::Union(..) - | hir::ItemKind::Use(..) => { - if item.vis.node.is_pub() { - self.prev_level - } else { - None - } + let impl_level = + Option::<AccessLevel>::of_impl(item.def_id, self.tcx, &self.access_levels); + self.update(item.def_id, impl_level) } + _ => self.get(item.def_id), }; - // Update level of the item itself. - let item_level = self.update(item.def_id, inherited_item_level); - // Update levels of nested things. match item.kind { hir::ItemKind::Enum(ref def, _) => { for variant in def.variants { - let variant_level = - self.update(self.tcx.hir().local_def_id(variant.id), item_level); + let variant_level = self.update_with_hir_id(variant.id, item_level); if let Some(ctor_hir_id) = variant.data.ctor_hir_id() { - self.update(self.tcx.hir().local_def_id(ctor_hir_id), item_level); + self.update_with_hir_id(ctor_hir_id, item_level); } for field in variant.data.fields() { - self.update(self.tcx.hir().local_def_id(field.hir_id), variant_level); + self.update_with_hir_id(field.hir_id, variant_level); } } } @@ -752,11 +681,11 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { } hir::ItemKind::Struct(ref def, _) | hir::ItemKind::Union(ref def, _) => { if let Some(ctor_hir_id) = def.ctor_hir_id() { - self.update(self.tcx.hir().local_def_id(ctor_hir_id), item_level); + self.update_with_hir_id(ctor_hir_id, item_level); } for field in def.fields() { if field.vis.node.is_pub() { - self.update(self.tcx.hir().local_def_id(field.hir_id), item_level); + self.update_with_hir_id(field.hir_id, item_level); } } } @@ -789,14 +718,8 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { hir::ItemKind::Macro(..) | hir::ItemKind::ExternCrate(..) => {} // All nested items are checked by `visit_item`. hir::ItemKind::Mod(..) => {} - // Re-exports are handled in `visit_mod`. However, in order to avoid looping over - // all of the items of a mod in `visit_mod` looking for use statements, we handle - // making sure that intermediate use statements have their visibilities updated here. - hir::ItemKind::Use(path, _) => { - if item_level.is_some() { - self.update_visibility_of_intermediate_use_statements(path.segments.as_ref()); - } - } + // Handled in the access level of in rustc_resolve + hir::ItemKind::Use(..) => {} // The interface is empty. hir::ItemKind::GlobalAsm(..) => {} hir::ItemKind::OpaqueTy(..) => { @@ -920,27 +843,6 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { intravisit::walk_block(self, b); self.prev_level = orig_level; } - - fn visit_mod(&mut self, m: &'tcx hir::Mod<'tcx>, _sp: Span, id: hir::HirId) { - // This code is here instead of in visit_item so that the - // crate module gets processed as well. - if self.prev_level.is_some() { - let def_id = self.tcx.hir().local_def_id(id); - if let Some(exports) = self.tcx.module_exports(def_id) { - for export in exports.iter() { - if export.vis.is_public() { - if let Some(def_id) = export.res.opt_def_id() { - if let Some(def_id) = def_id.as_local() { - self.update(def_id, Some(AccessLevel::Exported)); - } - } - } - } - } - } - - intravisit::walk_mod(self, m, id); - } } impl ReachEverythingInTheInterfaceVisitor<'_, '_> { @@ -1045,7 +947,7 @@ impl<'tcx> NamePrivacyVisitor<'tcx> { let def_id = self.tcx.adjust_ident_and_get_scope(ident, def.did, hir_id).1; if !field.vis.is_accessible_from(def_id, self.tcx) { let label = if in_update_syntax { - format!("field `{}` is private", field.ident) + format!("field `{}` is private", field.name) } else { "private field".to_string() }; @@ -1055,7 +957,7 @@ impl<'tcx> NamePrivacyVisitor<'tcx> { span, E0451, "field `{}` of {} `{}` is private", - field.ident, + field.name, def.variant_descr(), self.tcx.def_path_str(def.did) ) @@ -2166,11 +2068,12 @@ fn privacy_access_levels(tcx: TyCtxt<'_>, (): ()) -> &AccessLevels { // items which are reachable from external crates based on visibility. let mut visitor = EmbargoVisitor { tcx, - access_levels: Default::default(), + access_levels: tcx.resolutions(()).access_levels.clone(), macro_reachable: Default::default(), prev_level: Some(AccessLevel::Public), changed: false, }; + loop { tcx.hir().walk_toplevel_module(&mut visitor); if visitor.changed { @@ -2179,7 +2082,6 @@ fn privacy_access_levels(tcx: TyCtxt<'_>, (): ()) -> &AccessLevels { break; } } - visitor.update(CRATE_DEF_ID, Some(AccessLevel::Public)); tcx.arena.alloc(visitor.access_levels) } diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 7bc3fd718e0..a080b4a3e9a 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -9,6 +9,7 @@ use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, Lrc, Ordering}; use rustc_index::vec::IndexVec; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; use smallvec::{smallvec, SmallVec}; +use std::assert_matches::assert_matches; use std::collections::hash_map::Entry; use std::fmt::Debug; use std::hash::Hash; @@ -165,7 +166,11 @@ impl<K: DepKind> DepGraph<K> { pub fn assert_ignored(&self) { if let Some(..) = self.data { K::read_deps(|task_deps| { - assert!(task_deps.is_none(), "expected no task dependency tracking"); + assert_matches!( + task_deps, + TaskDepsRef::Ignore, + "expected no task dependency tracking" + ); }) } } @@ -174,7 +179,60 @@ impl<K: DepKind> DepGraph<K> { where OP: FnOnce() -> R, { - K::with_deps(None, op) + K::with_deps(TaskDepsRef::Ignore, op) + } + + /// Used to wrap the deserialization of a query result from disk, + /// This method enforces that no new `DepNodes` are created during + /// query result deserialization. + /// + /// Enforcing this makes the query dep graph simpler - all nodes + /// must be created during the query execution, and should be + /// created from inside the 'body' of a query (the implementation + /// provided by a particular compiler crate). + /// + /// Consider the case of three queries `A`, `B`, and `C`, where + /// `A` invokes `B` and `B` invokes `C`: + /// + /// `A -> B -> C` + /// + /// Suppose that decoding the result of query `B` required re-computing + /// the query `C`. If we did not create a fresh `TaskDeps` when + /// decoding `B`, we would still be using the `TaskDeps` for query `A` + /// (if we needed to re-execute `A`). This would cause us to create + /// a new edge `A -> C`. If this edge did not previously + /// exist in the `DepGraph`, then we could end up with a different + /// `DepGraph` at the end of compilation, even if there were no + /// meaningful changes to the overall program (e.g. a newline was added). + /// In addition, this edge might cause a subsequent compilation run + /// to try to force `C` before marking other necessary nodes green. If + /// `C` did not exist in the new compilation session, then we could + /// get an ICE. Normally, we would have tried (and failed) to mark + /// some other query green (e.g. `item_children`) which was used + /// to obtain `C`, which would prevent us from ever trying to force + /// a non-existent `D`. + /// + /// It might be possible to enforce that all `DepNode`s read during + /// deserialization already exist in the previous `DepGraph`. In + /// the above example, we would invoke `D` during the deserialization + /// of `B`. Since we correctly create a new `TaskDeps` from the decoding + /// of `B`, this would result in an edge `B -> D`. If that edge already + /// existed (with the same `DepPathHash`es), then it should be correct + /// to allow the invocation of the query to proceed during deserialization + /// of a query result. We would merely assert that the dep-graph fragment + /// that would have been added by invoking `C` while decoding `B` + /// is equivalent to the dep-graph fragment that we already instantiated for B + /// (at the point where we successfully marked B as green). + /// + /// However, this would require additional complexity + /// in the query infrastructure, and is not currently needed by the + /// decoding of any query results. Should the need arise in the future, + /// we should consider extending the query system with this functionality. + pub fn with_query_deserialization<OP, R>(&self, op: OP) -> R + where + OP: FnOnce() -> R, + { + K::with_deps(TaskDepsRef::Forbid, op) } /// Starts a new dep-graph task. Dep-graph tasks are specified @@ -259,7 +317,13 @@ impl<K: DepKind> DepGraph<K> { phantom_data: PhantomData, })) }; - let result = K::with_deps(task_deps.as_ref(), || task(cx, arg)); + + let task_deps_ref = match &task_deps { + Some(deps) => TaskDepsRef::Allow(deps), + None => TaskDepsRef::Ignore, + }; + + let result = K::with_deps(task_deps_ref, || task(cx, arg)); let edges = task_deps.map_or_else(|| smallvec![], |lock| lock.into_inner().reads); let dcx = cx.dep_context(); @@ -312,7 +376,7 @@ impl<K: DepKind> DepGraph<K> { if let Some(ref data) = self.data { let task_deps = Lock::new(TaskDeps::default()); - let result = K::with_deps(Some(&task_deps), op); + let result = K::with_deps(TaskDepsRef::Allow(&task_deps), op); let task_deps = task_deps.into_inner(); let task_deps = task_deps.reads; @@ -365,42 +429,47 @@ impl<K: DepKind> DepGraph<K> { pub fn read_index(&self, dep_node_index: DepNodeIndex) { if let Some(ref data) = self.data { K::read_deps(|task_deps| { - if let Some(task_deps) = task_deps { - let mut task_deps = task_deps.lock(); - let task_deps = &mut *task_deps; - if cfg!(debug_assertions) { - data.current.total_read_count.fetch_add(1, Relaxed); + let mut task_deps = match task_deps { + TaskDepsRef::Allow(deps) => deps.lock(), + TaskDepsRef::Ignore => return, + TaskDepsRef::Forbid => { + panic!("Illegal read of: {:?}", dep_node_index) } + }; + let task_deps = &mut *task_deps; - // As long as we only have a low number of reads we can avoid doing a hash - // insert and potentially allocating/reallocating the hashmap - let new_read = if task_deps.reads.len() < TASK_DEPS_READS_CAP { - task_deps.reads.iter().all(|other| *other != dep_node_index) - } else { - task_deps.read_set.insert(dep_node_index) - }; - if new_read { - task_deps.reads.push(dep_node_index); - if task_deps.reads.len() == TASK_DEPS_READS_CAP { - // Fill `read_set` with what we have so far so we can use the hashset - // next time - task_deps.read_set.extend(task_deps.reads.iter().copied()); - } + if cfg!(debug_assertions) { + data.current.total_read_count.fetch_add(1, Relaxed); + } - #[cfg(debug_assertions)] - { - if let Some(target) = task_deps.node { - if let Some(ref forbidden_edge) = data.current.forbidden_edge { - let src = forbidden_edge.index_to_node.lock()[&dep_node_index]; - if forbidden_edge.test(&src, &target) { - panic!("forbidden edge {:?} -> {:?} created", src, target) - } + // As long as we only have a low number of reads we can avoid doing a hash + // insert and potentially allocating/reallocating the hashmap + let new_read = if task_deps.reads.len() < TASK_DEPS_READS_CAP { + task_deps.reads.iter().all(|other| *other != dep_node_index) + } else { + task_deps.read_set.insert(dep_node_index) + }; + if new_read { + task_deps.reads.push(dep_node_index); + if task_deps.reads.len() == TASK_DEPS_READS_CAP { + // Fill `read_set` with what we have so far so we can use the hashset + // next time + task_deps.read_set.extend(task_deps.reads.iter().copied()); + } + + #[cfg(debug_assertions)] + { + if let Some(target) = task_deps.node { + if let Some(ref forbidden_edge) = data.current.forbidden_edge { + let src = forbidden_edge.index_to_node.lock()[&dep_node_index]; + if forbidden_edge.test(&src, &target) { + panic!("forbidden edge {:?} -> {:?} created", src, target) } } } - } else if cfg!(debug_assertions) { - data.current.total_duplicate_read_count.fetch_add(1, Relaxed); } + } else if cfg!(debug_assertions) { + data.current.total_duplicate_read_count.fetch_add(1, Relaxed); } }) } @@ -1123,7 +1192,26 @@ impl<K: DepKind> CurrentDepGraph<K> { const TASK_DEPS_READS_CAP: usize = 8; type EdgesVec = SmallVec<[DepNodeIndex; TASK_DEPS_READS_CAP]>; -pub struct TaskDeps<K> { +#[derive(Debug, Clone, Copy)] +pub enum TaskDepsRef<'a, K: DepKind> { + /// New dependencies can be added to the + /// `TaskDeps`. This is used when executing a 'normal' query + /// (no `eval_always` modifier) + Allow(&'a Lock<TaskDeps<K>>), + /// New dependencies are ignored. This is used when + /// executing an `eval_always` query, since there's no + /// need to track dependencies for a query that's always + /// re-executed. This is also used for `dep_graph.with_ignore` + Ignore, + /// Any attempt to add new dependencies will cause a panic. + /// This is used when decoding a query result from disk, + /// to ensure that the decoding process doesn't itself + /// require the execution of any queries. + Forbid, +} + +#[derive(Debug)] +pub struct TaskDeps<K: DepKind> { #[cfg(debug_assertions)] node: Option<DepNode<K>>, reads: EdgesVec, @@ -1131,7 +1219,7 @@ pub struct TaskDeps<K> { phantom_data: PhantomData<DepNode<K>>, } -impl<K> Default for TaskDeps<K> { +impl<K: DepKind> Default for TaskDeps<K> { fn default() -> Self { Self { #[cfg(debug_assertions)] diff --git a/compiler/rustc_query_system/src/dep_graph/mod.rs b/compiler/rustc_query_system/src/dep_graph/mod.rs index 047fc9f10cc..5907ae309ca 100644 --- a/compiler/rustc_query_system/src/dep_graph/mod.rs +++ b/compiler/rustc_query_system/src/dep_graph/mod.rs @@ -5,13 +5,14 @@ mod query; mod serialized; pub use dep_node::{DepNode, DepNodeParams, WorkProductId}; -pub use graph::{hash_result, DepGraph, DepNodeColor, DepNodeIndex, TaskDeps, WorkProduct}; +pub use graph::{ + hash_result, DepGraph, DepNodeColor, DepNodeIndex, TaskDeps, TaskDepsRef, WorkProduct, +}; pub use query::DepGraphQuery; pub use serialized::{SerializedDepGraph, SerializedDepNodeIndex}; use crate::ich::StableHashingContext; use rustc_data_structures::profiling::SelfProfilerRef; -use rustc_data_structures::sync::Lock; use rustc_serialize::{opaque::FileEncoder, Encodable}; use rustc_session::Session; @@ -90,12 +91,12 @@ pub trait DepKind: Copy + fmt::Debug + Eq + Hash + Send + Encodable<FileEncoder> fn debug_node(node: &DepNode<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result; /// Execute the operation with provided dependencies. - fn with_deps<OP, R>(deps: Option<&Lock<TaskDeps<Self>>>, op: OP) -> R + fn with_deps<OP, R>(deps: TaskDepsRef<'_, Self>, op: OP) -> R where OP: FnOnce() -> R; /// Access dependencies from current implicit context. fn read_deps<OP>(op: OP) where - OP: for<'a> FnOnce(Option<&'a Lock<TaskDeps<Self>>>); + OP: for<'a> FnOnce(TaskDepsRef<'a, Self>); } diff --git a/compiler/rustc_query_system/src/ich/hcx.rs b/compiler/rustc_query_system/src/ich/hcx.rs index 5f31fa04b8a..76e21be17bc 100644 --- a/compiler/rustc_query_system/src/ich/hcx.rs +++ b/compiler/rustc_query_system/src/ich/hcx.rs @@ -3,6 +3,7 @@ use rustc_ast as ast; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::stable_hasher::{HashingControls, NodeIdHashingMode}; use rustc_data_structures::sync::Lrc; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -26,20 +27,15 @@ fn compute_ignored_attr_names() -> FxHashSet<Symbol> { pub struct StableHashingContext<'a> { definitions: &'a Definitions, cstore: &'a dyn CrateStore, + // The value of `-Z incremental-ignore-spans`. + // This field should only be used by `debug_opts_incremental_ignore_span` + incremental_ignore_spans: bool, pub(super) body_resolver: BodyResolver<'a>, - hash_spans: bool, - pub(super) node_id_hashing_mode: NodeIdHashingMode, - // Very often, we are hashing something that does not need the // `CachingSourceMapView`, so we initialize it lazily. raw_source_map: &'a SourceMap, caching_source_map: Option<CachingSourceMapView<'a>>, -} - -#[derive(PartialEq, Eq, Clone, Copy)] -pub enum NodeIdHashingMode { - Ignore, - HashDefPath, + pub(super) hashing_controls: HashingControls, } /// The `BodyResolver` allows mapping a `BodyId` to the corresponding `hir::Body`. @@ -70,10 +66,13 @@ impl<'a> StableHashingContext<'a> { body_resolver: BodyResolver::Forbidden, definitions, cstore, + incremental_ignore_spans: sess.opts.debugging_opts.incremental_ignore_spans, caching_source_map: None, raw_source_map: sess.source_map(), - hash_spans: hash_spans_initial, - node_id_hashing_mode: NodeIdHashingMode::HashDefPath, + hashing_controls: HashingControls { + hash_spans: hash_spans_initial, + node_id_hashing_mode: NodeIdHashingMode::HashDefPath, + }, } } @@ -133,10 +132,10 @@ impl<'a> StableHashingContext<'a> { #[inline] pub fn while_hashing_spans<F: FnOnce(&mut Self)>(&mut self, hash_spans: bool, f: F) { - let prev_hash_spans = self.hash_spans; - self.hash_spans = hash_spans; + let prev_hash_spans = self.hashing_controls.hash_spans; + self.hashing_controls.hash_spans = hash_spans; f(self); - self.hash_spans = prev_hash_spans; + self.hashing_controls.hash_spans = prev_hash_spans; } #[inline] @@ -145,10 +144,10 @@ impl<'a> StableHashingContext<'a> { mode: NodeIdHashingMode, f: F, ) { - let prev = self.node_id_hashing_mode; - self.node_id_hashing_mode = mode; + let prev = self.hashing_controls.node_id_hashing_mode; + self.hashing_controls.node_id_hashing_mode = mode; f(self); - self.node_id_hashing_mode = prev; + self.hashing_controls.node_id_hashing_mode = prev; } #[inline] @@ -183,6 +182,11 @@ impl<'a> StableHashingContext<'a> { } IGNORED_ATTRIBUTES.with(|attrs| attrs.contains(&name)) } + + #[inline] + pub fn hashing_controls(&self) -> HashingControls { + self.hashing_controls.clone() + } } impl<'a> HashStable<StableHashingContext<'a>> for ast::NodeId { @@ -195,7 +199,12 @@ impl<'a> HashStable<StableHashingContext<'a>> for ast::NodeId { impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> { #[inline] fn hash_spans(&self) -> bool { - self.hash_spans + self.hashing_controls.hash_spans + } + + #[inline] + fn debug_opts_incremental_ignore_spans(&self) -> bool { + self.incremental_ignore_spans } #[inline] @@ -215,6 +224,11 @@ impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> { ) -> Option<(Lrc<SourceFile>, usize, BytePos, usize, BytePos)> { self.source_map().span_data_to_lines_and_cols(span) } + + #[inline] + fn hashing_controls(&self) -> HashingControls { + self.hashing_controls.clone() + } } impl<'a> rustc_session::HashStableContext for StableHashingContext<'a> {} diff --git a/compiler/rustc_query_system/src/ich/impls_hir.rs b/compiler/rustc_query_system/src/ich/impls_hir.rs index 3117140a5b6..bf3cf6a48fd 100644 --- a/compiler/rustc_query_system/src/ich/impls_hir.rs +++ b/compiler/rustc_query_system/src/ich/impls_hir.rs @@ -11,7 +11,7 @@ impl<'ctx> rustc_hir::HashStableContext for StableHashingContext<'ctx> { #[inline] fn hash_hir_id(&mut self, hir_id: hir::HirId, hasher: &mut StableHasher) { let hcx = self; - match hcx.node_id_hashing_mode { + match hcx.hashing_controls.node_id_hashing_mode { NodeIdHashingMode::Ignore => { // Don't do anything. } @@ -89,12 +89,12 @@ impl<'ctx> rustc_hir::HashStableContext for StableHashingContext<'ctx> { #[inline] fn hash_hir_item_like<F: FnOnce(&mut Self)>(&mut self, f: F) { - let prev_hash_node_ids = self.node_id_hashing_mode; - self.node_id_hashing_mode = NodeIdHashingMode::Ignore; + let prev_hash_node_ids = self.hashing_controls.node_id_hashing_mode; + self.hashing_controls.node_id_hashing_mode = NodeIdHashingMode::Ignore; f(self); - self.node_id_hashing_mode = prev_hash_node_ids; + self.hashing_controls.node_id_hashing_mode = prev_hash_node_ids; } #[inline] diff --git a/compiler/rustc_query_system/src/ich/mod.rs b/compiler/rustc_query_system/src/ich/mod.rs index 54416902e5f..c42fcc9c82e 100644 --- a/compiler/rustc_query_system/src/ich/mod.rs +++ b/compiler/rustc_query_system/src/ich/mod.rs @@ -1,6 +1,7 @@ //! ICH - Incremental Compilation Hash -pub use self::hcx::{NodeIdHashingMode, StableHashingContext}; +pub use self::hcx::StableHashingContext; +pub use rustc_data_structures::stable_hasher::NodeIdHashingMode; use rustc_span::symbol::{sym, Symbol}; mod hcx; diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 33732f9df73..da1f3617647 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -9,7 +9,6 @@ use crate::query::job::{ report_cycle, QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryShardJobId, }; use crate::query::{QueryContext, QueryMap, QuerySideEffects, QueryStackFrame}; - use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxHasher}; #[cfg(parallel_compiler)] @@ -515,7 +514,13 @@ where // Some things are never cached on disk. if query.cache_on_disk { let prof_timer = tcx.dep_context().profiler().incr_cache_loading(); - let result = query.try_load_from_disk(tcx, prev_dep_node_index); + + // The call to `with_query_deserialization` enforces that no new `DepNodes` + // are created during deserialization. See the docs of that method for more + // details. + let result = dep_graph + .with_query_deserialization(|| query.try_load_from_disk(tcx, prev_dep_node_index)); + prof_timer.finish_with_query_invocation_id(dep_node_index.into()); if let Some(result) = result { diff --git a/compiler/rustc_resolve/src/access_levels.rs b/compiler/rustc_resolve/src/access_levels.rs new file mode 100644 index 00000000000..60cc4248edc --- /dev/null +++ b/compiler/rustc_resolve/src/access_levels.rs @@ -0,0 +1,237 @@ +use rustc_ast::ast; +use rustc_ast::visit; +use rustc_ast::visit::Visitor; +use rustc_ast::Crate; +use rustc_ast::EnumDef; +use rustc_ast::ForeignMod; +use rustc_ast::NodeId; +use rustc_ast_lowering::ResolverAstLowering; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::def_id::CRATE_DEF_ID; +use rustc_middle::middle::privacy::AccessLevel; +use rustc_middle::ty::Visibility; +use rustc_span::sym; + +use crate::imports::ImportKind; +use crate::BindingKey; +use crate::NameBinding; +use crate::NameBindingKind; +use crate::Resolver; + +pub struct AccessLevelsVisitor<'r, 'a> { + r: &'r mut Resolver<'a>, + prev_level: Option<AccessLevel>, + changed: bool, +} + +impl<'r, 'a> AccessLevelsVisitor<'r, 'a> { + /// Fills the `Resolver::access_levels` table with public & exported items + /// For now, this doesn't resolve macros (FIXME) and cannot resolve Impl, as we + /// need access to a TyCtxt for that. + pub fn compute_access_levels<'c>(r: &'r mut Resolver<'a>, krate: &'c Crate) { + let mut visitor = + AccessLevelsVisitor { r, changed: false, prev_level: Some(AccessLevel::Public) }; + + visitor.set_access_level_def_id(CRATE_DEF_ID, Some(AccessLevel::Public)); + visitor.set_exports_access_level(CRATE_DEF_ID); + + while visitor.changed { + visitor.reset(); + visit::walk_crate(&mut visitor, krate); + } + + tracing::info!("resolve::access_levels: {:#?}", r.access_levels); + } + + fn reset(&mut self) { + self.changed = false; + self.prev_level = Some(AccessLevel::Public); + } + + /// Update the access level of the exports of the given module accordingly. The module access + /// level has to be Exported or Public. + /// This will also follow `use` chains (see PrivacyVisitor::set_import_binding_access_level). + fn set_exports_access_level(&mut self, module_id: LocalDefId) { + assert!(self.r.module_map.contains_key(&&module_id.to_def_id())); + + // Set the given binding access level to `AccessLevel::Public` and + // sets the rest of the `use` chain to `AccessLevel::Exported` until + // we hit the actual exported item. + let set_import_binding_access_level = + |this: &mut Self, mut binding: &NameBinding<'a>, mut access_level| { + while let NameBindingKind::Import { binding: nested_binding, import, .. } = + binding.kind + { + this.set_access_level(import.id, access_level); + if let ImportKind::Single { additional_ids, .. } = import.kind { + this.set_access_level(additional_ids.0, access_level); + this.set_access_level(additional_ids.1, access_level); + } + + access_level = Some(AccessLevel::Exported); + binding = nested_binding; + } + }; + + let module_level = self.r.access_levels.map.get(&module_id).copied(); + assert!(module_level >= Some(AccessLevel::Exported)); + + if let Some(exports) = self.r.reexport_map.get(&module_id) { + let pub_exports = exports + .iter() + .filter(|ex| ex.vis == Visibility::Public) + .cloned() + .collect::<Vec<_>>(); + + let module = self.r.get_module(module_id.to_def_id()).unwrap(); + for export in pub_exports.into_iter() { + if let Some(export_def_id) = export.res.opt_def_id().and_then(|id| id.as_local()) { + self.set_access_level_def_id(export_def_id, Some(AccessLevel::Exported)); + } + + if let Some(ns) = export.res.ns() { + let key = BindingKey { ident: export.ident, ns, disambiguator: 0 }; + let name_res = self.r.resolution(module, key); + if let Some(binding) = name_res.borrow().binding() { + set_import_binding_access_level(self, binding, module_level) + } + } + } + } + } + + /// Sets the access level of the `LocalDefId` corresponding to the given `NodeId`. + /// This function will panic if the `NodeId` does not have a `LocalDefId` + fn set_access_level( + &mut self, + node_id: NodeId, + access_level: Option<AccessLevel>, + ) -> Option<AccessLevel> { + self.set_access_level_def_id(self.r.local_def_id(node_id), access_level) + } + + fn set_access_level_def_id( + &mut self, + def_id: LocalDefId, + access_level: Option<AccessLevel>, + ) -> Option<AccessLevel> { + let old_level = self.r.access_levels.map.get(&def_id).copied(); + if old_level < access_level { + self.r.access_levels.map.insert(def_id, access_level.unwrap()); + self.changed = true; + access_level + } else { + old_level + } + } +} + +impl<'r, 'ast> Visitor<'ast> for AccessLevelsVisitor<'ast, 'r> { + fn visit_item(&mut self, item: &'ast ast::Item) { + let inherited_item_level = match item.kind { + // Resolved in rustc_privacy when types are available + ast::ItemKind::Impl(..) => return, + + // Only exported `macro_rules!` items are public, but they always are + ast::ItemKind::MacroDef(..) => { + let is_macro_export = + item.attrs.iter().any(|attr| attr.has_name(sym::macro_export)); + if is_macro_export { Some(AccessLevel::Public) } else { None } + } + + // Foreign modules inherit level from parents. + ast::ItemKind::ForeignMod(..) => self.prev_level, + + // Other `pub` items inherit levels from parents. + ast::ItemKind::ExternCrate(..) + | ast::ItemKind::Use(..) + | ast::ItemKind::Static(..) + | ast::ItemKind::Const(..) + | ast::ItemKind::Fn(..) + | ast::ItemKind::Mod(..) + | ast::ItemKind::GlobalAsm(..) + | ast::ItemKind::TyAlias(..) + | ast::ItemKind::Enum(..) + | ast::ItemKind::Struct(..) + | ast::ItemKind::Union(..) + | ast::ItemKind::Trait(..) + | ast::ItemKind::TraitAlias(..) => { + if item.vis.kind.is_pub() { + self.prev_level + } else { + None + } + } + + // Should be unreachable at this stage + ast::ItemKind::MacCall(..) => panic!( + "ast::ItemKind::MacCall encountered, this should not anymore appear at this stage" + ), + }; + + let access_level = self.set_access_level(item.id, inherited_item_level); + + // Set access level of nested items. + // If it's a mod, also make the visitor walk all of its items + match item.kind { + ast::ItemKind::Mod(..) => { + if access_level.is_some() { + self.set_exports_access_level(self.r.local_def_id(item.id)); + } + + let orig_level = std::mem::replace(&mut self.prev_level, access_level); + visit::walk_item(self, item); + self.prev_level = orig_level; + } + + ast::ItemKind::ForeignMod(ForeignMod { ref items, .. }) => { + for nested in items { + if nested.vis.kind.is_pub() { + self.set_access_level(nested.id, access_level); + } + } + } + ast::ItemKind::Enum(EnumDef { ref variants }, _) => { + for variant in variants { + let variant_level = self.set_access_level(variant.id, access_level); + if let Some(ctor_id) = variant.data.ctor_id() { + self.set_access_level(ctor_id, access_level); + } + + for field in variant.data.fields() { + self.set_access_level(field.id, variant_level); + } + } + } + ast::ItemKind::Struct(ref def, _) | ast::ItemKind::Union(ref def, _) => { + if let Some(ctor_id) = def.ctor_id() { + self.set_access_level(ctor_id, access_level); + } + + for field in def.fields() { + if field.vis.kind.is_pub() { + self.set_access_level(field.id, access_level); + } + } + } + ast::ItemKind::Trait(ref trait_kind) => { + for nested in trait_kind.items.iter() { + self.set_access_level(nested.id, access_level); + } + } + + ast::ItemKind::ExternCrate(..) + | ast::ItemKind::Use(..) + | ast::ItemKind::Static(..) + | ast::ItemKind::Const(..) + | ast::ItemKind::GlobalAsm(..) + | ast::ItemKind::TyAlias(..) + | ast::ItemKind::TraitAlias(..) + | ast::ItemKind::MacroDef(..) + | ast::ItemKind::Fn(..) => return, + + // Unreachable kinds + ast::ItemKind::Impl(..) | ast::ItemKind::MacCall(..) => unreachable!(), + } + } +} diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 39074f811a5..052770b201a 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -26,7 +26,7 @@ use rustc_hir::def::{self, *}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_INDEX}; use rustc_metadata::creader::LoadedMacro; use rustc_middle::bug; -use rustc_middle::hir::exports::Export; +use rustc_middle::metadata::ModChild; use rustc_middle::ty; use rustc_session::cstore::CrateStore; use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind}; @@ -214,7 +214,7 @@ impl<'a> Resolver<'a> { } crate fn build_reduced_graph_external(&mut self, module: Module<'a>) { - for child in self.cstore().item_children_untracked(module.def_id(), self.session) { + for child in self.cstore().module_children_untracked(module.def_id(), self.session) { let parent_scope = ParentScope::module(module, self); BuildReducedGraphVisitor { r: self, parent_scope } .build_reduced_graph_for_external_crate_res(child); @@ -383,8 +383,6 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { used: Cell::new(false), }); - debug!("add_import({:?})", import); - self.r.indeterminate_imports.push(import); match import.kind { // Don't add unresolved underscore imports to modules @@ -455,7 +453,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { prefix.is_empty() || prefix.len() == 1 && prefix[0].ident.name == kw::PathRoot }; match use_tree.kind { - ast::UseTreeKind::Simple(rename, ..) => { + ast::UseTreeKind::Simple(rename, id1, id2) => { let mut ident = use_tree.ident(); let mut module_path = prefix; let mut source = module_path.pop().unwrap(); @@ -565,7 +563,9 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { }, type_ns_only, nested, + additional_ids: (id1, id2), }; + self.add_import( module_path, kind, @@ -938,9 +938,9 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { } /// Builds the reduced graph for a single item in an external crate. - fn build_reduced_graph_for_external_crate_res(&mut self, child: Export) { + fn build_reduced_graph_for_external_crate_res(&mut self, child: ModChild) { let parent = self.parent_scope.module; - let Export { ident, res, vis, span } = child; + let ModChild { ident, res, vis, span } = child; let res = res.expect_non_local(); let expansion = self.parent_scope.expansion; // Record primary definitions. diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index bf4cece8bde..e7f76a18ad3 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -15,7 +15,7 @@ use rustc_data_structures::ptr_key::PtrKey; use rustc_errors::{pluralize, struct_span_err, Applicability}; use rustc_hir::def::{self, PartialRes}; use rustc_hir::def_id::DefId; -use rustc_middle::hir::exports::Export; +use rustc_middle::metadata::ModChild; use rustc_middle::span_bug; use rustc_middle::ty; use rustc_session::lint::builtin::{PUB_USE_OF_PRIVATE_EXTERN_CRATE, UNUSED_IMPORTS}; @@ -48,6 +48,9 @@ pub enum ImportKind<'a> { type_ns_only: bool, /// Did this import result from a nested import? ie. `use foo::{bar, baz};` nested: bool, + /// Additional `NodeId`s allocated to a `ast::UseTree` for automatically generated `use` statement + /// (eg. implicit struct constructors) + additional_ids: (NodeId, NodeId), }, Glob { is_prelude: bool, @@ -834,7 +837,6 @@ impl<'a, 'b> ImportResolver<'a, 'b> { import.span, ); import.vis.set(orig_vis); - source_bindings[ns].set(binding); } else { return; @@ -1409,7 +1411,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { if is_good_import || binding.is_macro_def() { let res = binding.res().expect_non_local(); if res != def::Res::Err { - reexports.push(Export { ident, res, span: binding.span, vis: binding.vis }); + reexports.push(ModChild { ident, res, vis: binding.vis, span: binding.span }); } } }); @@ -1418,7 +1420,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { if let Some(def_id) = module.opt_def_id() { // Call to `expect_local` should be fine because current // code is only called for local modules. - self.r.export_map.insert(def_id.expect_local(), reexports); + self.r.reexport_map.insert(def_id.expect_local(), reexports); } } } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 57305023138..1b02511fd7c 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -520,9 +520,16 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { } fn visit_fn(&mut self, fn_kind: FnKind<'ast>, sp: Span, _: NodeId) { let rib_kind = match fn_kind { - // Bail if there's no body. - FnKind::Fn(.., None) => return visit::walk_fn(self, fn_kind, sp), - FnKind::Fn(FnCtxt::Free | FnCtxt::Foreign, ..) => FnItemRibKind, + // Bail if the function is foreign, and thus cannot validly have + // a body, or if there's no body for some other reason. + FnKind::Fn(FnCtxt::Foreign, _, sig, ..) | FnKind::Fn(_, _, sig, .., None) => { + // We don't need to deal with patterns in parameters, because + // they are not possible for foreign or bodiless functions. + self.visit_fn_header(&sig.header); + visit::walk_fn_decl(self, &sig.decl); + return; + } + FnKind::Fn(FnCtxt::Free, ..) => FnItemRibKind, FnKind::Fn(FnCtxt::Assoc(_), ..) => NormalRibKind, FnKind::Closure(..) => ClosureOrAsyncRibKind, }; diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index b46a93c0673..28437742758 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -49,7 +49,8 @@ use rustc_hir::definitions::{DefKey, DefPathData, Definitions}; use rustc_hir::TraitCandidate; use rustc_index::vec::IndexVec; use rustc_metadata::creader::{CStore, CrateLoader}; -use rustc_middle::hir::exports::ExportMap; +use rustc_middle::metadata::ModChild; +use rustc_middle::middle::privacy::AccessLevels; use rustc_middle::span_bug; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, DefIdTree, MainDefinition, ResolverOutputs}; @@ -77,8 +78,11 @@ use imports::{Import, ImportKind, ImportResolver, NameResolution}; use late::{ConstantItemKind, HasGenericParams, PathSource, Rib, RibKind::*}; use macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef}; +use crate::access_levels::AccessLevelsVisitor; + type Res = def::Res<NodeId>; +mod access_levels; mod build_reduced_graph; mod check_unused; mod def_collector; @@ -927,7 +931,7 @@ pub struct Resolver<'a> { /// `CrateNum` resolutions of `extern crate` items. extern_crate_map: FxHashMap<LocalDefId, CrateNum>, - export_map: ExportMap, + reexport_map: FxHashMap<LocalDefId, Vec<ModChild>>, trait_map: NodeMap<Vec<TraitCandidate>>, /// A map from nodes to anonymous modules. @@ -1052,6 +1056,8 @@ pub struct Resolver<'a> { /// they are declared in the static array generated by proc_macro_harness. proc_macros: Vec<NodeId>, confused_type_with_std_module: FxHashMap<Span, Span>, + + access_levels: AccessLevels, } /// Nothing really interesting here; it just provides memory for the rest of the crate. @@ -1333,7 +1339,7 @@ impl<'a> Resolver<'a> { import_res_map: Default::default(), label_res_map: Default::default(), extern_crate_map: Default::default(), - export_map: FxHashMap::default(), + reexport_map: FxHashMap::default(), trait_map: NodeMap::default(), underscore_disambiguator: 0, empty_module, @@ -1407,6 +1413,7 @@ impl<'a> Resolver<'a> { trait_impls: Default::default(), proc_macros: Default::default(), confused_type_with_std_module: Default::default(), + access_levels: Default::default(), }; let root_parent_scope = ParentScope::module(graph_root, &resolver); @@ -1446,18 +1453,20 @@ impl<'a> Resolver<'a> { let definitions = self.definitions; let visibilities = self.visibilities; let extern_crate_map = self.extern_crate_map; - let export_map = self.export_map; + let reexport_map = self.reexport_map; let maybe_unused_trait_imports = self.maybe_unused_trait_imports; let maybe_unused_extern_crates = self.maybe_unused_extern_crates; let glob_map = self.glob_map; let main_def = self.main_def; let confused_type_with_std_module = self.confused_type_with_std_module; + let access_levels = self.access_levels; ResolverOutputs { definitions, cstore: Box::new(self.crate_loader.into_cstore()), visibilities, + access_levels, extern_crate_map, - export_map, + reexport_map, glob_map, maybe_unused_trait_imports, maybe_unused_extern_crates, @@ -1477,10 +1486,11 @@ impl<'a> Resolver<'a> { let proc_macros = self.proc_macros.iter().map(|id| self.local_def_id(*id)).collect(); ResolverOutputs { definitions: self.definitions.clone(), + access_levels: self.access_levels.clone(), cstore: Box::new(self.cstore().clone()), visibilities: self.visibilities.clone(), extern_crate_map: self.extern_crate_map.clone(), - export_map: self.export_map.clone(), + reexport_map: self.reexport_map.clone(), glob_map: self.glob_map.clone(), maybe_unused_trait_imports: self.maybe_unused_trait_imports.clone(), maybe_unused_extern_crates: self.maybe_unused_extern_crates.clone(), @@ -1532,6 +1542,9 @@ impl<'a> Resolver<'a> { pub fn resolve_crate(&mut self, krate: &Crate) { self.session.time("resolve_crate", || { self.session.time("finalize_imports", || ImportResolver { r: self }.finalize_imports()); + self.session.time("resolve_access_levels", || { + AccessLevelsVisitor::compute_access_levels(self, krate) + }); self.session.time("finalize_macro_resolutions", || self.finalize_macro_resolutions()); self.session.time("late_resolve_crate", || self.late_resolve_crate(krate)); self.session.time("resolve_main", || self.resolve_main()); diff --git a/compiler/rustc_save_analysis/src/lib.rs b/compiler/rustc_save_analysis/src/lib.rs index 7ec619e07ff..a83f0230814 100644 --- a/compiler/rustc_save_analysis/src/lib.rs +++ b/compiler/rustc_save_analysis/src/lib.rs @@ -710,13 +710,11 @@ impl<'tcx> SaveContext<'tcx> { } Res::Def(HirDefKind::AssocFn, decl_id) => { let def_id = if decl_id.is_local() { - let ti = self.tcx.associated_item(decl_id); - - self.tcx - .associated_items(ti.container.id()) - .filter_by_name_unhygienic(ti.ident.name) - .find(|item| item.defaultness.has_value()) - .map(|item| item.def_id) + if self.tcx.associated_item(decl_id).defaultness.has_value() { + Some(decl_id) + } else { + None + } } else { None }; diff --git a/compiler/rustc_serialize/src/leb128.rs b/compiler/rustc_serialize/src/leb128.rs index ea2df80e641..08b3c054200 100644 --- a/compiler/rustc_serialize/src/leb128.rs +++ b/compiler/rustc_serialize/src/leb128.rs @@ -53,16 +53,24 @@ impl_write_unsigned_leb128!(write_usize_leb128, usize); macro_rules! impl_read_unsigned_leb128 { ($fn_name:ident, $int_ty:ty) => { #[inline] - pub fn $fn_name(slice: &[u8]) -> ($int_ty, usize) { - let mut result = 0; - let mut shift = 0; - let mut position = 0; + pub fn $fn_name(slice: &[u8], position: &mut usize) -> $int_ty { + // The first iteration of this loop is unpeeled. This is a + // performance win because this code is hot and integer values less + // than 128 are very common, typically occurring 50-80% or more of + // the time, even for u64 and u128. + let byte = slice[*position]; + *position += 1; + if (byte & 0x80) == 0 { + return byte as $int_ty; + } + let mut result = (byte & 0x7F) as $int_ty; + let mut shift = 7; loop { - let byte = slice[position]; - position += 1; + let byte = slice[*position]; + *position += 1; if (byte & 0x80) == 0 { result |= (byte as $int_ty) << shift; - return (result, position); + return result; } else { result |= ((byte & 0x7F) as $int_ty) << shift; } @@ -122,15 +130,14 @@ impl_write_signed_leb128!(write_isize_leb128, isize); macro_rules! impl_read_signed_leb128 { ($fn_name:ident, $int_ty:ty) => { #[inline] - pub fn $fn_name(slice: &[u8]) -> ($int_ty, usize) { + pub fn $fn_name(slice: &[u8], position: &mut usize) -> $int_ty { let mut result = 0; let mut shift = 0; - let mut position = 0; let mut byte; loop { - byte = slice[position]; - position += 1; + byte = slice[*position]; + *position += 1; result |= <$int_ty>::from(byte & 0x7F) << shift; shift += 7; @@ -144,7 +151,7 @@ macro_rules! impl_read_signed_leb128 { result |= (!0 << shift); } - (result, position) + result } }; } diff --git a/compiler/rustc_serialize/src/opaque.rs b/compiler/rustc_serialize/src/opaque.rs index f2ef1481681..078237801be 100644 --- a/compiler/rustc_serialize/src/opaque.rs +++ b/compiler/rustc_serialize/src/opaque.rs @@ -560,11 +560,7 @@ impl<'a> Decoder<'a> { } macro_rules! read_leb128 { - ($dec:expr, $fun:ident) => {{ - let (value, bytes_read) = leb128::$fun(&$dec.data[$dec.position..]); - $dec.position += bytes_read; - Ok(value) - }}; + ($dec:expr, $fun:ident) => {{ Ok(leb128::$fun($dec.data, &mut $dec.position)) }}; } impl<'a> serialize::Decoder for Decoder<'a> { diff --git a/compiler/rustc_serialize/tests/leb128.rs b/compiler/rustc_serialize/tests/leb128.rs index 3e2aab5125a..314c07db981 100644 --- a/compiler/rustc_serialize/tests/leb128.rs +++ b/compiler/rustc_serialize/tests/leb128.rs @@ -30,9 +30,8 @@ macro_rules! impl_test_unsigned_leb128 { let mut position = 0; for &expected in &values { - let (actual, bytes_read) = $read_fn_name(&stream[position..]); + let actual = $read_fn_name(&stream, &mut position); assert_eq!(expected, actual); - position += bytes_read; } assert_eq!(stream.len(), position); } @@ -77,9 +76,8 @@ macro_rules! impl_test_signed_leb128 { let mut position = 0; for &expected in &values { - let (actual, bytes_read) = $read_fn_name(&stream[position..]); + let actual = $read_fn_name(&stream, &mut position); assert_eq!(expected, actual); - position += bytes_read; } assert_eq!(stream.len(), position); } diff --git a/compiler/rustc_span/src/def_id.rs b/compiler/rustc_span/src/def_id.rs index f6b0785a07c..0bba918994c 100644 --- a/compiler/rustc_span/src/def_id.rs +++ b/compiler/rustc_span/src/def_id.rs @@ -136,8 +136,8 @@ impl Borrow<Fingerprint> for DefPathHash { /// collisions when loading crates and abort compilation in order to avoid /// further trouble. /// -/// See the discussion in [`DefId`] for more information -/// on the possibility of hash collisions in rustc, +/// For more information on the possibility of hash collisions in rustc, +/// see the discussion in [`DefId`]. #[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug)] #[derive(HashStable_Generic, Encodable, Decodable)] pub struct StableCrateId(pub(crate) u64); @@ -221,10 +221,17 @@ impl<D: Decoder> Decodable<D> for DefIndex { #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Copy)] // On below-64 bit systems we can simply use the derived `Hash` impl #[cfg_attr(not(target_pointer_width = "64"), derive(Hash))] -// Note that the order is essential here, see below why +#[repr(C)] +// We guarantee field order. Note that the order is essential here, see below why. pub struct DefId { + // cfg-ing the order of fields so that the `DefIndex` which is high entropy always ends up in + // the lower bits no matter the endianness. This allows the compiler to turn that `Hash` impl + // into a direct call to 'u64::hash(_)`. + #[cfg(not(all(target_pointer_width = "64", target_endian = "big")))] pub index: DefIndex, pub krate: CrateNum, + #[cfg(all(target_pointer_width = "64", target_endian = "big"))] + pub index: DefIndex, } // On 64-bit systems, we can hash the whole `DefId` as one `u64` instead of two `u32`s. This diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 315b706fbc4..7b70c20d307 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -32,6 +32,7 @@ use crate::{HashStableContext, Span, DUMMY_SP}; use crate::def_id::{CrateNum, DefId, StableCrateId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::stable_hasher::HashingControls; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::{Lock, Lrc}; use rustc_data_structures::unhash::UnhashMap; @@ -88,6 +89,33 @@ rustc_index::newtype_index! { } } +/// Assert that the provided `HashStableContext` is configured with the 'default' +/// `HashingControls`. We should always have bailed out before getting to here +/// with a non-default mode. With this check in place, we can avoid the need +/// to maintain separate versions of `ExpnData` hashes for each permutation +/// of `HashingControls` settings. +fn assert_default_hashing_controls<CTX: HashStableContext>(ctx: &CTX, msg: &str) { + match ctx.hashing_controls() { + // Ideally, we would also check that `node_id_hashing_mode` was always + // `NodeIdHashingMode::HashDefPath`. However, we currently end up hashing + // `Span`s in this mode, and there's not an easy way to change that. + // All of the span-related data that we hash is pretty self-contained + // (in particular, we don't hash any `HirId`s), so this shouldn't result + // in any caching problems. + // FIXME: Enforce that we don't end up transitively hashing any `HirId`s, + // or ensure that this method is always invoked with the same + // `NodeIdHashingMode` + // + // Note that we require that `hash_spans` be set according to the global + // `-Z incremental-ignore-spans` option. Normally, this option is disabled, + // which will cause us to require that this method always be called with `Span` hashing + // enabled. + HashingControls { hash_spans, node_id_hashing_mode: _ } + if hash_spans == !ctx.debug_opts_incremental_ignore_spans() => {} + other => panic!("Attempted hashing of {msg} with non-default HashingControls: {:?}", other), + } +} + /// A unique hash value associated to an expansion. #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encodable, Decodable, HashStable_Generic)] pub struct ExpnHash(Fingerprint); @@ -1444,6 +1472,7 @@ fn update_disambiguator(expn_data: &mut ExpnData, mut ctx: impl HashStableContex "Already set disambiguator for ExpnData: {:?}", expn_data ); + assert_default_hashing_controls(&ctx, "ExpnData (disambiguator)"); let mut expn_hash = expn_data.hash_expn(&mut ctx); let disambiguator = HygieneData::with(|data| { @@ -1493,6 +1522,7 @@ impl<CTX: HashStableContext> HashStable<CTX> for SyntaxContext { impl<CTX: HashStableContext> HashStable<CTX> for ExpnId { fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { + assert_default_hashing_controls(ctx, "ExpnId"); let hash = if *self == ExpnId::root() { // Avoid fetching TLS storage for a trivial often-used value. Fingerprint::ZERO diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 3bbf2a0e456..9602bc5d0b7 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -42,6 +42,7 @@ pub mod hygiene; use hygiene::Transparency; pub use hygiene::{DesugaringKind, ExpnKind, MacroKind}; pub use hygiene::{ExpnData, ExpnHash, ExpnId, LocalExpnId, SyntaxContext}; +use rustc_data_structures::stable_hasher::HashingControls; pub mod def_id; use def_id::{CrateNum, DefId, DefPathHash, LocalDefId, LOCAL_CRATE}; pub mod lev_distance; @@ -2057,11 +2058,15 @@ impl InnerSpan { pub trait HashStableContext { fn def_path_hash(&self, def_id: DefId) -> DefPathHash; fn hash_spans(&self) -> bool; + /// Accesses `sess.opts.debugging_opts.incremental_ignore_spans` since + /// we don't have easy access to a `Session` + fn debug_opts_incremental_ignore_spans(&self) -> bool; fn def_span(&self, def_id: LocalDefId) -> Span; fn span_data_to_lines_and_cols( &mut self, span: &SpanData, ) -> Option<(Lrc<SourceFile>, usize, BytePos, usize, BytePos)>; + fn hashing_controls(&self) -> HashingControls; } impl<CTX> HashStable<CTX> for Span diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 84cf8878af8..f99d5cfad0a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -208,6 +208,7 @@ symbols! { LinkedList, LintPass, Mutex, + N, None, Ok, Option, @@ -327,6 +328,7 @@ symbols! { array, arrays, as_ptr, + as_ref, as_str, asm, asm_const, @@ -567,6 +569,7 @@ symbols! { doc_spotlight, doctest, document_private_items, + dotdot: "..", dotdot_in_tuple_patterns, dotdoteq_in_patterns, dreg, @@ -592,6 +595,7 @@ symbols! { enable, enclosing_scope, encode, + end, env, eq, ermsb_target_feature, diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index eebf618a5de..6d02d04fe80 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -113,29 +113,29 @@ fn get_symbol_hash<'tcx>( hcx.while_hashing_spans(false, |hcx| { hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { item_type.hash_stable(hcx, &mut hasher); - }); - }); - // If this is a function, we hash the signature as well. - // This is not *strictly* needed, but it may help in some - // situations, see the `run-make/a-b-a-linker-guard` test. - if let ty::FnDef(..) = item_type.kind() { - item_type.fn_sig(tcx).hash_stable(&mut hcx, &mut hasher); - } + // If this is a function, we hash the signature as well. + // This is not *strictly* needed, but it may help in some + // situations, see the `run-make/a-b-a-linker-guard` test. + if let ty::FnDef(..) = item_type.kind() { + item_type.fn_sig(tcx).hash_stable(hcx, &mut hasher); + } - // also include any type parameters (for generic items) - substs.hash_stable(&mut hcx, &mut hasher); + // also include any type parameters (for generic items) + substs.hash_stable(hcx, &mut hasher); - if let Some(instantiating_crate) = instantiating_crate { - tcx.def_path_hash(instantiating_crate.as_def_id()) - .stable_crate_id() - .hash_stable(&mut hcx, &mut hasher); - } + if let Some(instantiating_crate) = instantiating_crate { + tcx.def_path_hash(instantiating_crate.as_def_id()) + .stable_crate_id() + .hash_stable(hcx, &mut hasher); + } - // We want to avoid accidental collision between different types of instances. - // Especially, `VtableShim`s and `ReifyShim`s may overlap with their original - // instances without this. - discriminant(&instance.def).hash_stable(&mut hcx, &mut hasher); + // We want to avoid accidental collision between different types of instances. + // Especially, `VtableShim`s and `ReifyShim`s may overlap with their original + // instances without this. + discriminant(&instance.def).hash_stable(hcx, &mut hasher); + }); + }); }); // 64 bits should be enough to avoid collisions. diff --git a/compiler/rustc_target/src/spec/avr_gnu_base.rs b/compiler/rustc_target/src/spec/avr_gnu_base.rs index 2cb2661a526..a6c1b344d70 100644 --- a/compiler/rustc_target/src/spec/avr_gnu_base.rs +++ b/compiler/rustc_target/src/spec/avr_gnu_base.rs @@ -17,12 +17,10 @@ pub fn target(target_cpu: String) -> Target { linker: Some("avr-gcc".to_owned()), executables: true, eh_frame_header: false, - pre_link_args: vec![(LinkerFlavor::Gcc, vec![format!("-mmcu={}", target_cpu)])] - .into_iter() - .collect(), - late_link_args: vec![(LinkerFlavor::Gcc, vec!["-lgcc".to_owned()])] + pre_link_args: [(LinkerFlavor::Gcc, vec![format!("-mmcu={}", target_cpu)])] .into_iter() .collect(), + late_link_args: [(LinkerFlavor::Gcc, vec!["-lgcc".to_owned()])].into_iter().collect(), max_atomic_width: Some(0), atomic_cas: false, ..TargetOptions::default() diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index ca1949b9f75..2c149318730 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -574,15 +574,15 @@ impl ToJson for StackProbeType { fn to_json(&self) -> Json { Json::Object(match self { StackProbeType::None => { - vec![(String::from("kind"), "none".to_json())].into_iter().collect() + [(String::from("kind"), "none".to_json())].into_iter().collect() } StackProbeType::Inline => { - vec![(String::from("kind"), "inline".to_json())].into_iter().collect() + [(String::from("kind"), "inline".to_json())].into_iter().collect() } StackProbeType::Call => { - vec![(String::from("kind"), "call".to_json())].into_iter().collect() + [(String::from("kind"), "call".to_json())].into_iter().collect() } - StackProbeType::InlineOrCall { min_llvm_version_for_inline } => vec![ + StackProbeType::InlineOrCall { min_llvm_version_for_inline } => [ (String::from("kind"), "inline-or-call".to_json()), ( String::from("min-llvm-version-for-inline"), diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index a9ae0ec53c7..72878b6cb38 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -2247,7 +2247,7 @@ pub fn recursive_type_with_infinite_size_error( spans .iter() .flat_map(|&span| { - vec![ + [ (span.shrink_to_lo(), "Box<".to_string()), (span.shrink_to_hi(), ">".to_string()), ] 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 0f276718c16..8704c4c7469 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1226,7 +1226,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { .returns .iter() .flat_map(|expr| { - vec![ + [ (expr.span.shrink_to_lo(), "Box::new(".to_string()), (expr.span.shrink_to_hi(), ")".to_string()), ] diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 23f615a9618..577b96f3a40 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1225,6 +1225,10 @@ fn assemble_candidates_from_object_ty<'cx, 'tcx>( ); } +#[tracing::instrument( + level = "debug", + skip(selcx, candidate_set, ctor, env_predicates, potentially_unnormalized_candidates) +)] fn assemble_candidates_from_predicates<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, @@ -1233,8 +1237,6 @@ fn assemble_candidates_from_predicates<'cx, 'tcx>( env_predicates: impl Iterator<Item = ty::Predicate<'tcx>>, potentially_unnormalized_candidates: bool, ) { - debug!(?obligation, "assemble_candidates_from_predicates"); - let infcx = selcx.infcx(); for predicate in env_predicates { debug!(?predicate); @@ -1270,13 +1272,12 @@ fn assemble_candidates_from_predicates<'cx, 'tcx>( } } +#[tracing::instrument(level = "debug", skip(selcx, obligation, candidate_set))] fn assemble_candidates_from_impls<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, candidate_set: &mut ProjectionTyCandidateSet<'tcx>, ) { - debug!("assemble_candidates_from_impls"); - // If we are resolving `<T as TraitRef<...>>::Item == Type`, // start out by selecting the predicate `T as TraitRef<...>`: let poly_trait_ref = ty::Binder::dummy(obligation.predicate.trait_ref(selcx.tcx())); @@ -1400,8 +1401,17 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // Any type with multiple potential metadata types is therefore not eligible. let self_ty = selcx.infcx().shallow_resolve(obligation.predicate.self_ty()); - // FIXME: should this normalize? - let tail = selcx.tcx().struct_tail_without_normalization(self_ty); + let tail = selcx.tcx().struct_tail_with_normalize(self_ty, |ty| { + normalize_with_depth( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + ty, + ) + .value + }); + match tail.kind() { ty::Bool | ty::Char @@ -1435,7 +1445,12 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | ty::Bound(..) | ty::Placeholder(..) | ty::Infer(..) - | ty::Error(_) => false, + | ty::Error(_) => { + if tail.has_infer_types() { + candidate_set.mark_ambiguous(); + } + false + }, } } super::ImplSource::Param(..) => { @@ -1640,18 +1655,30 @@ fn confirm_pointee_candidate<'cx, 'tcx>( _: ImplSourcePointeeData, ) -> Progress<'tcx> { let tcx = selcx.tcx(); - let self_ty = selcx.infcx().shallow_resolve(obligation.predicate.self_ty()); - let substs = tcx.mk_substs([self_ty.into()].iter()); + let mut obligations = vec![]; + let metadata_ty = self_ty.ptr_metadata_ty(tcx, |ty| { + normalize_with_depth_to( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + ty, + &mut obligations, + ) + }); + + let substs = tcx.mk_substs([self_ty.into()].iter()); let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None); let predicate = ty::ProjectionPredicate { projection_ty: ty::ProjectionTy { substs, item_def_id: metadata_def_id }, - ty: self_ty.ptr_metadata_ty(tcx), + ty: metadata_ty, }; confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false) + .with_addl_obligations(obligations) } fn confirm_fn_pointer_candidate<'cx, 'tcx>( @@ -1883,7 +1910,6 @@ fn assoc_ty_def( assoc_ty_def_id: DefId, ) -> Result<specialization_graph::LeafDef, ErrorReported> { let tcx = selcx.tcx(); - let assoc_ty_name = tcx.associated_item(assoc_ty_def_id).ident; let trait_def_id = tcx.impl_trait_ref(impl_def_id).unwrap().def_id; let trait_def = tcx.trait_def(trait_def_id); @@ -1893,21 +1919,18 @@ fn assoc_ty_def( // for the associated item at the given impl. // If there is no such item in that impl, this function will fail with a // cycle error if the specialization graph is currently being built. - let impl_node = specialization_graph::Node::Impl(impl_def_id); - for item in impl_node.items(tcx) { - if matches!(item.kind, ty::AssocKind::Type) - && tcx.hygienic_eq(item.ident, assoc_ty_name, trait_def_id) - { - return Ok(specialization_graph::LeafDef { - item: *item, - defining_node: impl_node, - finalizing_node: if item.defaultness.is_default() { None } else { Some(impl_node) }, - }); - } + if let Some(&impl_item_id) = tcx.impl_item_implementor_ids(impl_def_id).get(&assoc_ty_def_id) { + let item = tcx.associated_item(impl_item_id); + let impl_node = specialization_graph::Node::Impl(impl_def_id); + return Ok(specialization_graph::LeafDef { + item: *item, + defining_node: impl_node, + finalizing_node: if item.defaultness.is_default() { None } else { Some(impl_node) }, + }); } let ancestors = trait_def.ancestors(tcx, impl_def_id)?; - if let Some(assoc_item) = ancestors.leaf_def(tcx, assoc_ty_name, ty::AssocKind::Type) { + if let Some(assoc_item) = ancestors.leaf_def(tcx, assoc_ty_def_id) { Ok(assoc_item) } else { // This is saying that neither the trait nor @@ -1916,7 +1939,11 @@ fn assoc_ty_def( // could only arise through a compiler bug -- // if the user wrote a bad item name, it // should have failed in astconv. - bug!("No associated type `{}` for {}", assoc_ty_name, tcx.def_path_str(impl_def_id)) + bug!( + "No associated type `{}` for {}", + tcx.item_name(assoc_ty_def_id), + tcx.def_path_str(impl_def_id) + ) } } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index 12ca3faeb37..d662f61e2cf 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -4,7 +4,9 @@ use crate::infer::canonical::{ use crate::infer::{InferCtxt, InferOk}; use crate::traits::query::Fallible; use crate::traits::ObligationCause; -use rustc_infer::infer::canonical::Canonical; +use rustc_infer::infer::canonical::{Canonical, Certainty}; +use rustc_infer::traits::query::NoSolution; +use rustc_infer::traits::PredicateObligations; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; use std::fmt; @@ -17,7 +19,6 @@ pub mod implied_outlives_bounds; pub mod normalize; pub mod outlives; pub mod prove_predicate; -use self::prove_predicate::ProvePredicate; pub mod subtype; pub use rustc_middle::traits::query::type_op::*; @@ -80,9 +81,14 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable<'tcx> + 'tcx { query_key: ParamEnvAnd<'tcx, Self>, infcx: &InferCtxt<'_, 'tcx>, output_query_region_constraints: &mut QueryRegionConstraints<'tcx>, - ) -> Fallible<(Self::QueryResponse, Option<Canonical<'tcx, ParamEnvAnd<'tcx, Self>>>)> { + ) -> Fallible<( + Self::QueryResponse, + Option<Canonical<'tcx, ParamEnvAnd<'tcx, Self>>>, + PredicateObligations<'tcx>, + Certainty, + )> { if let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &query_key) { - return Ok((result, None)); + return Ok((result, None, vec![], Certainty::Proven)); } // FIXME(#33684) -- We need to use @@ -104,20 +110,7 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable<'tcx> + 'tcx { output_query_region_constraints, )?; - // Typically, instantiating NLL query results does not - // create obligations. However, in some cases there - // are unresolved type variables, and unify them *can* - // create obligations. In that case, we have to go - // fulfill them. We do this via a (recursive) query. - for obligation in obligations { - let ((), _) = ProvePredicate::fully_perform_into( - obligation.param_env.and(ProvePredicate::new(obligation.predicate)), - infcx, - output_query_region_constraints, - )?; - } - - Ok((value, Some(canonical_self))) + Ok((value, Some(canonical_self), obligations, canonical_result.value.certainty)) } } @@ -129,9 +122,39 @@ where fn fully_perform(self, infcx: &InferCtxt<'_, 'tcx>) -> Fallible<TypeOpOutput<'tcx, Self>> { let mut region_constraints = QueryRegionConstraints::default(); - let (output, canonicalized_query) = + let (output, canonicalized_query, mut obligations, _) = Q::fully_perform_into(self, infcx, &mut region_constraints)?; + // Typically, instantiating NLL query results does not + // create obligations. However, in some cases there + // are unresolved type variables, and unify them *can* + // create obligations. In that case, we have to go + // fulfill them. We do this via a (recursive) query. + while !obligations.is_empty() { + trace!("{:#?}", obligations); + let mut progress = false; + for obligation in std::mem::take(&mut obligations) { + let obligation = infcx.resolve_vars_if_possible(obligation); + match ProvePredicate::fully_perform_into( + obligation.param_env.and(ProvePredicate::new(obligation.predicate)), + infcx, + &mut region_constraints, + ) { + Ok(((), _, new, certainty)) => { + obligations.extend(new); + progress = true; + if let Certainty::Ambiguous = certainty { + obligations.push(obligation); + } + } + Err(_) => obligations.push(obligation), + } + } + if !progress { + return Err(NoSolution); + } + } + // Promote the final query-region-constraints into a // (optional) ref-counted vector: let region_constraints = diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 017f47d4357..b573c4b4390 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -173,6 +173,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let needs_infer = stack.obligation.predicate.has_infer_types_or_consts(); + let sized_predicate = self.tcx().lang_items().sized_trait() + == Some(stack.obligation.predicate.skip_binder().def_id()); + // If there are STILL multiple candidates, we can further // reduce the list by dropping duplicates -- including // resolving specializations. @@ -181,6 +184,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { while i < candidates.len() { let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| { self.candidate_should_be_dropped_in_favor_of( + sized_predicate, &candidates[i], &candidates[j], needs_infer, @@ -338,13 +342,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(candidates) } + #[tracing::instrument(level = "debug", skip(self, candidates))] fn assemble_candidates_from_projected_tys( &mut self, obligation: &TraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { - debug!(?obligation, "assemble_candidates_from_projected_tys"); - // Before we go into the whole placeholder thing, just // quickly check if the self-type is a projection at all. match obligation.predicate.skip_binder().trait_ref.self_ty().kind() { @@ -369,12 +372,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// supplied to find out whether it is listed among them. /// /// Never affects the inference environment. + #[tracing::instrument(level = "debug", skip(self, stack, candidates))] fn assemble_candidates_from_caller_bounds<'o>( &mut self, stack: &TraitObligationStack<'o, 'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) -> Result<(), SelectionError<'tcx>> { - debug!(?stack.obligation, "assemble_candidates_from_caller_bounds"); + debug!(?stack.obligation); let all_bounds = stack .obligation @@ -876,6 +880,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }; } + #[tracing::instrument(level = "debug", skip(self, obligation, candidates))] fn assemble_candidates_for_trait_alias( &mut self, obligation: &TraitObligation<'tcx>, @@ -883,7 +888,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) { // Okay to skip binder here because the tests we do below do not involve bound regions. let self_ty = obligation.self_ty().skip_binder(); - debug!(?self_ty, "assemble_candidates_for_trait_alias"); + debug!(?self_ty); let def_id = obligation.predicate.def_id(); @@ -894,6 +899,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// Assembles the trait which are built-in to the language itself: /// `Copy`, `Clone` and `Sized`. + #[tracing::instrument(level = "debug", skip(self, candidates))] fn assemble_builtin_bound_candidates( &mut self, conditions: BuiltinImplConditions<'tcx>, @@ -901,14 +907,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) { match conditions { BuiltinImplConditions::Where(nested) => { - debug!(?nested, "builtin_bound"); candidates .vec .push(BuiltinCandidate { has_nested: !nested.skip_binder().is_empty() }); } BuiltinImplConditions::None => {} BuiltinImplConditions::Ambiguous => { - debug!("assemble_builtin_bound_candidates: ambiguous builtin"); candidates.ambiguous = true; } } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index fa88c8ee370..32518ffb071 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -201,6 +201,7 @@ struct EvaluatedCandidate<'tcx> { } /// When does the builtin impl for `T: Trait` apply? +#[derive(Debug)] enum BuiltinImplConditions<'tcx> { /// The impl is conditional on `T1, T2, ...: Trait`. Where(ty::Binder<'tcx, Vec<Ty<'tcx>>>), @@ -344,7 +345,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } Err(e) => Err(e), Ok(candidate) => { - debug!(?candidate); + debug!(?candidate, "confirmed"); Ok(Some(candidate)) } } @@ -1523,6 +1524,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// See the comment for "SelectionCandidate" for more details. fn candidate_should_be_dropped_in_favor_of( &mut self, + sized_predicate: bool, victim: &EvaluatedCandidate<'tcx>, other: &EvaluatedCandidate<'tcx>, needs_infer: bool, @@ -1594,6 +1596,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Drop otherwise equivalent non-const fn pointer candidates (FnPointerCandidate { .. }, FnPointerCandidate { is_const: false }) => true, + // If obligation is a sized predicate or the where-clause bound is + // global, prefer the projection or object candidate. See issue + // #50825 and #89352. + (ObjectCandidate(_) | ProjectionCandidate(_), ParamCandidate(ref cand)) => { + sized_predicate || is_global(cand) + } + (ParamCandidate(ref cand), ObjectCandidate(_) | ProjectionCandidate(_)) => { + !(sized_predicate || is_global(cand)) + } + // Global bounds from the where clause should be ignored // here (see issue #50825). Otherwise, we have a where // clause so don't go around looking for impls. @@ -1609,15 +1621,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { .. } - | TraitAliasCandidate(..) - | ObjectCandidate(_) - | ProjectionCandidate(_), + | TraitAliasCandidate(..), ) => !is_global(cand), - (ObjectCandidate(_) | ProjectionCandidate(_), ParamCandidate(ref cand)) => { - // Prefer these to a global where-clause bound - // (see issue #50825). - is_global(cand) - } ( ImplCandidate(_) | ClosureCandidate @@ -1953,7 +1958,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::Generator(_, ref substs, _) => { let ty = self.infcx.shallow_resolve(substs.as_generator().tupled_upvars_ty()); let witness = substs.as_generator().witness(); - t.rebind(vec![ty].into_iter().chain(iter::once(witness)).collect()) + t.rebind([ty].into_iter().chain(iter::once(witness)).collect()) } ty::GeneratorWitness(types) => { diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 4bd73ef68aa..72ffe9085cb 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -197,14 +197,13 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( item: Option<&hir::Item<'tcx>>, cause: &mut traits::ObligationCause<'tcx>, pred: &ty::Predicate<'tcx>, - mut trait_assoc_items: impl Iterator<Item = &'tcx ty::AssocItem>, ) { debug!( "extended_cause_with_original_assoc_item_obligation {:?} {:?} {:?} {:?}", trait_ref, item, cause, pred ); - let items = match item { - Some(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) => impl_.items, + let (items, impl_def_id) = match item { + Some(hir::Item { kind: hir::ItemKind::Impl(impl_), def_id, .. }) => (impl_.items, *def_id), _ => return, }; let fix_span = @@ -222,11 +221,16 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( // `src/test/ui/associated-types/point-at-type-on-obligation-failure.rs` and // `traits-assoc-type-in-supertrait-bad.rs`. if let ty::Projection(projection_ty) = proj.ty.kind() { - let trait_assoc_item = tcx.associated_item(projection_ty.item_def_id); - if let Some(impl_item_span) = - items.iter().find(|item| item.ident == trait_assoc_item.ident).map(fix_span) + if let Some(&impl_item_id) = + tcx.impl_item_implementor_ids(impl_def_id).get(&projection_ty.item_def_id) { - cause.span = impl_item_span; + if let Some(impl_item_span) = items + .iter() + .find(|item| item.id.def_id.to_def_id() == impl_item_id) + .map(fix_span) + { + cause.span = impl_item_span; + } } } } @@ -235,13 +239,16 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( // can be seen in `ui/associated-types/point-at-type-on-obligation-failure-2.rs`. debug!("extended_cause_with_original_assoc_item_obligation trait proj {:?}", pred); if let ty::Projection(ty::ProjectionTy { item_def_id, .. }) = *pred.self_ty().kind() { - if let Some(impl_item_span) = trait_assoc_items - .find(|i| i.def_id == item_def_id) - .and_then(|trait_assoc_item| { - items.iter().find(|i| i.ident == trait_assoc_item.ident).map(fix_span) - }) + if let Some(&impl_item_id) = + tcx.impl_item_implementor_ids(impl_def_id).get(&item_def_id) { - cause.span = impl_item_span; + if let Some(impl_item_span) = items + .iter() + .find(|item| item.id.def_id.to_def_id() == impl_item_id) + .map(fix_span) + { + cause.span = impl_item_span; + } } } } @@ -312,7 +319,6 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { item, &mut cause, &obligation.predicate, - tcx.associated_items(trait_ref.def_id).in_definition_order(), ); traits::Obligation::with_depth(cause, depth, param_env, obligation.predicate) }; diff --git a/compiler/rustc_traits/src/chalk/db.rs b/compiler/rustc_traits/src/chalk/db.rs index c38680651af..3f51442277f 100644 --- a/compiler/rustc_traits/src/chalk/db.rs +++ b/compiler/rustc_traits/src/chalk/db.rs @@ -436,23 +436,13 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t ) -> Arc<chalk_solve::rust_ir::AssociatedTyValue<RustInterner<'tcx>>> { let def_id = associated_ty_id.0; let assoc_item = self.interner.tcx.associated_item(def_id); - let (impl_id, trait_id) = match assoc_item.container { - AssocItemContainer::TraitContainer(def_id) => (def_id, def_id), - AssocItemContainer::ImplContainer(def_id) => { - (def_id, self.interner.tcx.impl_trait_ref(def_id).unwrap().def_id) - } - }; + let impl_id = assoc_item.container.id(); match assoc_item.kind { AssocKind::Type => {} _ => unimplemented!("Not possible??"), } - let trait_item = self - .interner - .tcx - .associated_items(trait_id) - .find_by_name_and_kind(self.interner.tcx, assoc_item.ident, assoc_item.kind, trait_id) - .unwrap(); + let trait_item_id = assoc_item.trait_item_def_id.expect("assoc_ty with no trait version"); let bound_vars = bound_vars_for_item(self.interner.tcx, def_id); let binders = binders_for(self.interner, bound_vars); let ty = self @@ -464,7 +454,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t Arc::new(chalk_solve::rust_ir::AssociatedTyValue { impl_id: chalk_ir::ImplId(impl_id), - associated_ty_id: chalk_ir::AssocTypeId(trait_item.def_id), + associated_ty_id: chalk_ir::AssocTypeId(trait_item_id), value: chalk_ir::Binders::new( binders, chalk_solve::rust_ir::AssociatedTyValueBound { ty }, diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs new file mode 100644 index 00000000000..b1d47f6c29a --- /dev/null +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -0,0 +1,239 @@ +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::struct_span_err; +use rustc_hir as hir; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_middle::ty::{self, TyCtxt, TypeFoldable}; + +pub fn provide(providers: &mut ty::query::Providers) { + *providers = ty::query::Providers { + associated_item, + associated_item_def_ids, + associated_items, + impl_item_implementor_ids, + trait_of_item, + ..*providers + }; +} + +fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] { + let item = tcx.hir().expect_item(def_id.expect_local()); + match item.kind { + hir::ItemKind::Trait(.., ref trait_item_refs) => tcx.arena.alloc_from_iter( + trait_item_refs.iter().map(|trait_item_ref| trait_item_ref.id.def_id.to_def_id()), + ), + hir::ItemKind::Impl(ref impl_) => tcx.arena.alloc_from_iter( + impl_.items.iter().map(|impl_item_ref| impl_item_ref.id.def_id.to_def_id()), + ), + hir::ItemKind::TraitAlias(..) => &[], + _ => span_bug!(item.span, "associated_item_def_ids: not impl or trait"), + } +} + +fn associated_items(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItems<'_> { + let items = tcx.associated_item_def_ids(def_id).iter().map(|did| tcx.associated_item(*did)); + ty::AssocItems::new(items) +} + +fn impl_item_implementor_ids(tcx: TyCtxt<'_>, impl_id: DefId) -> FxHashMap<DefId, DefId> { + tcx.associated_items(impl_id) + .in_definition_order() + .filter_map(|item| item.trait_item_def_id.map(|trait_item| (trait_item, item.def_id))) + .collect() +} + +/// If the given `DefId` describes an item belonging to a trait, +/// returns the `DefId` of the trait that the trait item belongs to; +/// otherwise, returns `None`. +fn trait_of_item(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> { + tcx.opt_associated_item(def_id).and_then(|associated_item| match associated_item.container { + ty::TraitContainer(def_id) => Some(def_id), + ty::ImplContainer(_) => None, + }) +} + +fn associated_item(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItem { + let id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + let parent_id = tcx.hir().get_parent_item(id); + let parent_def_id = tcx.hir().local_def_id(parent_id); + let parent_item = tcx.hir().expect_item(parent_def_id); + match parent_item.kind { + hir::ItemKind::Impl(ref impl_) => { + if let Some(impl_item_ref) = + impl_.items.iter().find(|i| i.id.def_id.to_def_id() == def_id) + { + let assoc_item = + associated_item_from_impl_item_ref(tcx, parent_def_id, impl_item_ref); + debug_assert_eq!(assoc_item.def_id, def_id); + return assoc_item; + } + } + + hir::ItemKind::Trait(.., ref trait_item_refs) => { + if let Some(trait_item_ref) = + trait_item_refs.iter().find(|i| i.id.def_id.to_def_id() == def_id) + { + let assoc_item = + associated_item_from_trait_item_ref(tcx, parent_def_id, trait_item_ref); + debug_assert_eq!(assoc_item.def_id, def_id); + return assoc_item; + } + } + + _ => {} + } + + span_bug!( + parent_item.span, + "unexpected parent of trait or impl item or item not found: {:?}", + parent_item.kind + ) +} + +fn associated_item_from_trait_item_ref( + tcx: TyCtxt<'_>, + parent_def_id: LocalDefId, + trait_item_ref: &hir::TraitItemRef, +) -> ty::AssocItem { + let def_id = trait_item_ref.id.def_id; + let (kind, has_self) = match trait_item_ref.kind { + hir::AssocItemKind::Const => (ty::AssocKind::Const, false), + hir::AssocItemKind::Fn { has_self } => (ty::AssocKind::Fn, has_self), + hir::AssocItemKind::Type => (ty::AssocKind::Type, false), + }; + + ty::AssocItem { + ident: trait_item_ref.ident, + kind, + vis: tcx.visibility(def_id), + defaultness: trait_item_ref.defaultness, + def_id: def_id.to_def_id(), + trait_item_def_id: Some(def_id.to_def_id()), + container: ty::TraitContainer(parent_def_id.to_def_id()), + fn_has_self_parameter: has_self, + } +} + +fn associated_item_from_impl_item_ref( + tcx: TyCtxt<'_>, + parent_def_id: LocalDefId, + impl_item_ref: &hir::ImplItemRef, +) -> ty::AssocItem { + let def_id = impl_item_ref.id.def_id; + let (kind, has_self) = match impl_item_ref.kind { + hir::AssocItemKind::Const => (ty::AssocKind::Const, false), + hir::AssocItemKind::Fn { has_self } => (ty::AssocKind::Fn, has_self), + hir::AssocItemKind::Type => (ty::AssocKind::Type, false), + }; + + let trait_item_def_id = impl_item_base_id(tcx, parent_def_id, impl_item_ref); + + ty::AssocItem { + ident: impl_item_ref.ident, + kind, + vis: tcx.visibility(def_id), + defaultness: impl_item_ref.defaultness, + def_id: def_id.to_def_id(), + trait_item_def_id, + container: ty::ImplContainer(parent_def_id.to_def_id()), + fn_has_self_parameter: has_self, + } +} + +fn impl_item_base_id<'tcx>( + tcx: TyCtxt<'tcx>, + parent_def_id: LocalDefId, + impl_item: &hir::ImplItemRef, +) -> Option<DefId> { + let impl_trait_ref = tcx.impl_trait_ref(parent_def_id)?; + + // If the trait reference itself is erroneous (so the compilation is going + // to fail), skip checking the items here -- the `impl_item` table in `tcx` + // isn't populated for such impls. + if impl_trait_ref.references_error() { + return None; + } + + // Locate trait items + let associated_items = tcx.associated_items(impl_trait_ref.def_id); + + // Match item against trait + let mut items = associated_items.filter_by_name(tcx, impl_item.ident, impl_trait_ref.def_id); + + let mut trait_item = items.next()?; + + let is_compatible = |ty: &&ty::AssocItem| match (ty.kind, &impl_item.kind) { + (ty::AssocKind::Const, hir::AssocItemKind::Const) => true, + (ty::AssocKind::Fn, hir::AssocItemKind::Fn { .. }) => true, + (ty::AssocKind::Type, hir::AssocItemKind::Type) => true, + _ => false, + }; + + // If we don't have a compatible item, we'll use the first one whose name matches + // to report an error. + let mut compatible_kind = is_compatible(&trait_item); + + if !compatible_kind { + if let Some(ty_trait_item) = items.find(is_compatible) { + compatible_kind = true; + trait_item = ty_trait_item; + } + } + + if compatible_kind { + Some(trait_item.def_id) + } else { + report_mismatch_error(tcx, trait_item.def_id, impl_trait_ref, impl_item); + None + } +} + +#[inline(never)] +#[cold] +fn report_mismatch_error<'tcx>( + tcx: TyCtxt<'tcx>, + trait_item_def_id: DefId, + impl_trait_ref: ty::TraitRef<'tcx>, + impl_item: &hir::ImplItemRef, +) { + let mut err = match impl_item.kind { + hir::AssocItemKind::Const => { + // Find associated const definition. + struct_span_err!( + tcx.sess, + impl_item.span, + E0323, + "item `{}` is an associated const, which doesn't match its trait `{}`", + impl_item.ident, + impl_trait_ref.print_only_trait_path() + ) + } + + hir::AssocItemKind::Fn { .. } => { + struct_span_err!( + tcx.sess, + impl_item.span, + E0324, + "item `{}` is an associated method, which doesn't match its trait `{}`", + impl_item.ident, + impl_trait_ref.print_only_trait_path() + ) + } + + hir::AssocItemKind::Type => { + struct_span_err!( + tcx.sess, + impl_item.span, + E0325, + "item `{}` is an associated type, which doesn't match its trait `{}`", + impl_item.ident, + impl_trait_ref.print_only_trait_path() + ) + } + }; + + err.span_label(impl_item.span, "does not match trait"); + if let Some(trait_span) = tcx.hir().span_if_local(trait_item_def_id) { + err.span_label(trait_span, "item in trait"); + } + err.emit(); +} diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 13ffb2a5adc..e0aea786b83 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -152,8 +152,7 @@ fn inner_resolve_instance<'tcx>( let result = if let Some(trait_def_id) = tcx.trait_of_item(def.did) { debug!(" => associated item, attempting to find impl in param_env {:#?}", param_env); - let item = tcx.associated_item(def.did); - resolve_associated_item(tcx, &item, param_env, trait_def_id, substs) + resolve_associated_item(tcx, def.did, param_env, trait_def_id, substs) } else { let ty = tcx.type_of(def.def_id_for_type_of()); let item_type = tcx.subst_and_normalize_erasing_regions(substs, param_env, ty); @@ -204,19 +203,12 @@ fn inner_resolve_instance<'tcx>( fn resolve_associated_item<'tcx>( tcx: TyCtxt<'tcx>, - trait_item: &ty::AssocItem, + trait_item_id: DefId, param_env: ty::ParamEnv<'tcx>, trait_id: DefId, rcvr_substs: SubstsRef<'tcx>, ) -> Result<Option<Instance<'tcx>>, ErrorReported> { - let def_id = trait_item.def_id; - debug!( - "resolve_associated_item(trait_item={:?}, \ - param_env={:?}, \ - trait_id={:?}, \ - rcvr_substs={:?})", - def_id, param_env, trait_id, rcvr_substs - ); + debug!(?trait_item_id, ?param_env, ?trait_id, ?rcvr_substs, "resolve_associated_item"); let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_substs); @@ -232,7 +224,7 @@ fn resolve_associated_item<'tcx>( traits::ImplSource::UserDefined(impl_data) => { debug!( "resolving ImplSource::UserDefined: {:?}, {:?}, {:?}, {:?}", - param_env, trait_item, rcvr_substs, impl_data + param_env, trait_item_id, rcvr_substs, impl_data ); assert!(!rcvr_substs.needs_infer()); assert!(!trait_ref.needs_infer()); @@ -241,9 +233,9 @@ fn resolve_associated_item<'tcx>( let trait_def = tcx.trait_def(trait_def_id); let leaf_def = trait_def .ancestors(tcx, impl_data.impl_def_id)? - .leaf_def(tcx, trait_item.ident, trait_item.kind) + .leaf_def(tcx, trait_item_id) .unwrap_or_else(|| { - bug!("{:?} not found in {:?}", trait_item, impl_data.impl_def_id); + bug!("{:?} not found in {:?}", trait_item_id, impl_data.impl_def_id); }); let substs = tcx.infer_ctxt().enter(|infcx| { @@ -297,22 +289,22 @@ fn resolve_associated_item<'tcx>( // performs (i.e. that the definition's type in the `impl` matches // the declaration in the `trait`), so that we can cheaply check // here if it failed, instead of approximating it. - if trait_item.kind == ty::AssocKind::Const - && trait_item.def_id != leaf_def.item.def_id + if leaf_def.item.kind == ty::AssocKind::Const + && trait_item_id != leaf_def.item.def_id && leaf_def.item.def_id.is_local() { let normalized_type_of = |def_id, substs| { tcx.subst_and_normalize_erasing_regions(substs, param_env, tcx.type_of(def_id)) }; - let original_ty = normalized_type_of(trait_item.def_id, rcvr_substs); + let original_ty = normalized_type_of(trait_item_id, rcvr_substs); let resolved_ty = normalized_type_of(leaf_def.item.def_id, substs); if original_ty != resolved_ty { let msg = format!( "Instance::resolve: inconsistent associated `const` type: \ was `{}: {}` but resolved to `{}: {}`", - tcx.def_path_str_with_substs(trait_item.def_id, rcvr_substs), + tcx.def_path_str_with_substs(trait_item_id, rcvr_substs), original_ty, tcx.def_path_str_with_substs(leaf_def.item.def_id, substs), resolved_ty, @@ -343,19 +335,22 @@ fn resolve_associated_item<'tcx>( } traits::ImplSource::FnPointer(ref data) => match data.fn_ty.kind() { ty::FnDef(..) | ty::FnPtr(..) => Some(Instance { - def: ty::InstanceDef::FnPtrShim(trait_item.def_id, data.fn_ty), + def: ty::InstanceDef::FnPtrShim(trait_item_id, data.fn_ty), substs: rcvr_substs, }), _ => None, }, traits::ImplSource::Object(ref data) => { - let index = traits::get_vtable_index_of_object_method(tcx, data, def_id); - Some(Instance { def: ty::InstanceDef::Virtual(def_id, index), substs: rcvr_substs }) + let index = traits::get_vtable_index_of_object_method(tcx, data, trait_item_id); + Some(Instance { + def: ty::InstanceDef::Virtual(trait_item_id, index), + substs: rcvr_substs, + }) } traits::ImplSource::Builtin(..) => { if Some(trait_ref.def_id) == tcx.lang_items().clone_trait() { // FIXME(eddyb) use lang items for methods instead of names. - let name = tcx.item_name(def_id); + let name = tcx.item_name(trait_item_id); if name == sym::clone { let self_ty = trait_ref.self_ty(); @@ -367,7 +362,7 @@ fn resolve_associated_item<'tcx>( }; Some(Instance { - def: ty::InstanceDef::CloneShim(def_id, self_ty), + def: ty::InstanceDef::CloneShim(trait_item_id, self_ty), substs: rcvr_substs, }) } else { @@ -375,7 +370,7 @@ fn resolve_associated_item<'tcx>( // Use the default `fn clone_from` from `trait Clone`. let substs = tcx.erase_regions(rcvr_substs); - Some(ty::Instance::new(def_id, substs)) + Some(ty::Instance::new(trait_item_id, substs)) } } else { None diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs index 60f8e196bcb..55e19990761 100644 --- a/compiler/rustc_ty_utils/src/lib.rs +++ b/compiler/rustc_ty_utils/src/lib.rs @@ -16,6 +16,7 @@ extern crate tracing; use rustc_middle::ty::query::Providers; +mod assoc; mod common_traits; pub mod instance; mod needs_drop; @@ -23,6 +24,7 @@ pub mod representability; mod ty; pub fn provide(providers: &mut Providers) { + assoc::provide(providers); common_traits::provide(providers); needs_drop::provide(providers); ty::provide(providers); diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 6c2657bd64b..8f50e3e0fe1 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -1,6 +1,6 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::DefId; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, Binder, Predicate, PredicateKind, ToPredicate, Ty, TyCtxt}; use rustc_span::{sym, Span}; @@ -71,90 +71,6 @@ fn sized_constraint_for_ty<'tcx>( result } -fn associated_item_from_trait_item_ref( - tcx: TyCtxt<'_>, - parent_def_id: LocalDefId, - trait_item_ref: &hir::TraitItemRef, -) -> ty::AssocItem { - let def_id = trait_item_ref.id.def_id; - let (kind, has_self) = match trait_item_ref.kind { - hir::AssocItemKind::Const => (ty::AssocKind::Const, false), - hir::AssocItemKind::Fn { has_self } => (ty::AssocKind::Fn, has_self), - hir::AssocItemKind::Type => (ty::AssocKind::Type, false), - }; - - ty::AssocItem { - ident: trait_item_ref.ident, - kind, - vis: tcx.visibility(def_id), - defaultness: trait_item_ref.defaultness, - def_id: def_id.to_def_id(), - container: ty::TraitContainer(parent_def_id.to_def_id()), - fn_has_self_parameter: has_self, - } -} - -fn associated_item_from_impl_item_ref( - tcx: TyCtxt<'_>, - parent_def_id: LocalDefId, - impl_item_ref: &hir::ImplItemRef, -) -> ty::AssocItem { - let def_id = impl_item_ref.id.def_id; - let (kind, has_self) = match impl_item_ref.kind { - hir::AssocItemKind::Const => (ty::AssocKind::Const, false), - hir::AssocItemKind::Fn { has_self } => (ty::AssocKind::Fn, has_self), - hir::AssocItemKind::Type => (ty::AssocKind::Type, false), - }; - - ty::AssocItem { - ident: impl_item_ref.ident, - kind, - vis: tcx.visibility(def_id), - defaultness: impl_item_ref.defaultness, - def_id: def_id.to_def_id(), - container: ty::ImplContainer(parent_def_id.to_def_id()), - fn_has_self_parameter: has_self, - } -} - -fn associated_item(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItem { - let id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); - let parent_id = tcx.hir().get_parent_item(id); - let parent_def_id = tcx.hir().local_def_id(parent_id); - let parent_item = tcx.hir().expect_item(parent_def_id); - match parent_item.kind { - hir::ItemKind::Impl(ref impl_) => { - if let Some(impl_item_ref) = - impl_.items.iter().find(|i| i.id.def_id.to_def_id() == def_id) - { - let assoc_item = - associated_item_from_impl_item_ref(tcx, parent_def_id, impl_item_ref); - debug_assert_eq!(assoc_item.def_id, def_id); - return assoc_item; - } - } - - hir::ItemKind::Trait(.., ref trait_item_refs) => { - if let Some(trait_item_ref) = - trait_item_refs.iter().find(|i| i.id.def_id.to_def_id() == def_id) - { - let assoc_item = - associated_item_from_trait_item_ref(tcx, parent_def_id, trait_item_ref); - debug_assert_eq!(assoc_item.def_id, def_id); - return assoc_item; - } - } - - _ => {} - } - - span_bug!( - parent_item.span, - "unexpected parent of trait or impl item or item not found: {:?}", - parent_item.kind - ) -} - fn impl_defaultness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::Defaultness { let item = tcx.hir().expect_item(def_id.expect_local()); if let hir::ItemKind::Impl(impl_) = &item.kind { @@ -197,25 +113,6 @@ fn adt_sized_constraint(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AdtSizedConstrain ty::AdtSizedConstraint(result) } -fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] { - let item = tcx.hir().expect_item(def_id.expect_local()); - match item.kind { - hir::ItemKind::Trait(.., ref trait_item_refs) => tcx.arena.alloc_from_iter( - trait_item_refs.iter().map(|trait_item_ref| trait_item_ref.id.def_id.to_def_id()), - ), - hir::ItemKind::Impl(ref impl_) => tcx.arena.alloc_from_iter( - impl_.items.iter().map(|impl_item_ref| impl_item_ref.id.def_id.to_def_id()), - ), - hir::ItemKind::TraitAlias(..) => &[], - _ => span_bug!(item.span, "associated_item_def_ids: not impl or trait"), - } -} - -fn associated_items(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItems<'_> { - let items = tcx.associated_item_def_ids(def_id).iter().map(|did| tcx.associated_item(*did)); - ty::AssocItems::new(items) -} - fn def_ident_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> { tcx.hir() .get_if_local(def_id) @@ -231,16 +128,6 @@ fn def_ident_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> { .map(|ident| ident.span) } -/// If the given `DefId` describes an item belonging to a trait, -/// returns the `DefId` of the trait that the trait item belongs to; -/// otherwise, returns `None`. -fn trait_of_item(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> { - tcx.opt_associated_item(def_id).and_then(|associated_item| match associated_item.container { - ty::TraitContainer(def_id) => Some(def_id), - ty::ImplContainer(_) => None, - }) -} - /// See `ParamEnv` struct definition for details. #[instrument(level = "debug", skip(tcx))] fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { @@ -620,14 +507,10 @@ pub fn conservative_is_privately_uninhabited_raw<'tcx>( pub fn provide(providers: &mut ty::query::Providers) { *providers = ty::query::Providers { asyncness, - associated_item, - associated_item_def_ids, - associated_items, adt_sized_constraint, def_ident_span, param_env, param_env_reveal_all_normalized, - trait_of_item, instance_def_size_estimate, issue33140_self_ty, impl_defaultness, diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 8226ffbccc4..34f93a517ce 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -288,7 +288,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// Given the type/lifetime/const arguments provided to some path (along with /// an implicit `Self`, if this is a trait reference), returns the complete /// set of substitutions. This may involve applying defaulted type parameters. - /// Also returns back constraints on associated types. + /// Constraints on associated typess are created from `create_assoc_bindings_for_generic_args`. /// /// Example: /// @@ -302,7 +302,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// which will have been resolved to a `def_id` /// 3. The `generic_args` contains info on the `<...>` contents. The `usize` type /// parameters are returned in the `SubstsRef`, the associated type bindings like - /// `Output = u32` are returned in the `Vec<ConvertedBinding...>` result. + /// `Output = u32` are returned from `create_assoc_bindings_for_generic_args`. /// /// Note that the type listing given here is *exactly* what the user provided. /// @@ -1727,7 +1727,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let variant_def = adt_def .variants .iter() - .find(|vd| tcx.hygienic_eq(assoc_ident, vd.ident, adt_def.did)); + .find(|vd| tcx.hygienic_eq(assoc_ident, vd.ident(tcx), adt_def.did)); if let Some(variant_def) = variant_def { if permit_variants { tcx.check_stability(variant_def.def_id, Some(hir_ref_id), span, None); @@ -1786,7 +1786,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { &adt_def .variants .iter() - .map(|variant| variant.ident.name) + .map(|variant| variant.name) .collect::<Vec<Symbol>>(), assoc_ident.name, None, diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index fd7b3a55dfb..de560d50795 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs @@ -626,6 +626,7 @@ pub(super) fn check_opaque_for_cycles<'tcx>( /// /// Without this check the above code is incorrectly accepted: we would ICE if /// some tried, for example, to clone an `Option<X<&mut ()>>`. +#[instrument(level = "debug", skip(tcx))] fn check_opaque_meets_bounds<'tcx>( tcx: TyCtxt<'tcx>, def_id: LocalDefId, @@ -633,17 +634,14 @@ fn check_opaque_meets_bounds<'tcx>( span: Span, origin: &hir::OpaqueTyOrigin, ) { - match origin { - // Checked when type checking the function containing them. - hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => return, - // Can have different predicates to their defining use - hir::OpaqueTyOrigin::TyAlias => {} - } - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let param_env = tcx.param_env(def_id); + let defining_use_anchor = match *origin { + hir::OpaqueTyOrigin::FnReturn(did) | hir::OpaqueTyOrigin::AsyncFn(did) => did, + hir::OpaqueTyOrigin::TyAlias => def_id, + }; + let param_env = tcx.param_env(defining_use_anchor); - tcx.infer_ctxt().enter(move |infcx| { + tcx.infer_ctxt().with_opaque_type_inference(defining_use_anchor).enter(move |infcx| { let inh = Inherited::new(infcx, def_id); let infcx = &inh.infcx; let opaque_ty = tcx.mk_opaque(def_id.to_def_id(), substs); @@ -656,16 +654,15 @@ fn check_opaque_meets_bounds<'tcx>( let opaque_type_map = infcx.inner.borrow().opaque_types.clone(); for (OpaqueTypeKey { def_id, substs }, opaque_defn) in opaque_type_map { - match infcx - .at(&misc_cause, param_env) - .eq(opaque_defn.concrete_ty, tcx.type_of(def_id).subst(tcx, substs)) - { + let hidden_type = tcx.type_of(def_id).subst(tcx, substs); + trace!(?hidden_type); + match infcx.at(&misc_cause, param_env).eq(opaque_defn.concrete_ty, hidden_type) { Ok(infer_ok) => inh.register_infer_ok_obligations(infer_ok), Err(ty_err) => tcx.sess.delay_span_bug( - opaque_defn.definition_span, + span, &format!( - "could not unify `{}` with revealed type:\n{}", - opaque_defn.concrete_ty, ty_err, + "could not check bounds on revealed type `{}`:\n{}", + hidden_type, ty_err, ), ), } @@ -678,10 +675,17 @@ fn check_opaque_meets_bounds<'tcx>( infcx.report_fulfillment_errors(&errors, None, false); } - // Finally, resolve all regions. This catches wily misuses of - // lifetime parameters. - let fcx = FnCtxt::new(&inh, param_env, hir_id); - fcx.regionck_item(hir_id, span, FxHashSet::default()); + match origin { + // Checked when type checking the function containing them. + hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => return, + // Can have different predicates to their defining use + hir::OpaqueTyOrigin::TyAlias => { + // Finally, resolve all regions. This catches wily misuses of + // lifetime parameters. + let fcx = FnCtxt::new(&inh, param_env, hir_id); + fcx.regionck_item(hir_id, span, FxHashSet::default()); + } + } }); } @@ -841,14 +845,8 @@ pub(super) fn check_specialization_validity<'tcx>( trait_def: &ty::TraitDef, trait_item: &ty::AssocItem, impl_id: DefId, - impl_item: &hir::ImplItem<'_>, + impl_item: &hir::ImplItemRef, ) { - let kind = match impl_item.kind { - hir::ImplItemKind::Const(..) => ty::AssocKind::Const, - hir::ImplItemKind::Fn(..) => ty::AssocKind::Fn, - hir::ImplItemKind::TyAlias(_) => ty::AssocKind::Type, - }; - let ancestors = match trait_def.ancestors(tcx, impl_id) { Ok(ancestors) => ancestors, Err(_) => return, @@ -857,7 +855,7 @@ pub(super) fn check_specialization_validity<'tcx>( if parent.is_from_trait() { None } else { - Some((parent, parent.item(tcx, trait_item.ident, kind, trait_def.def_id))) + Some((parent, parent.item(tcx, trait_item.def_id))) } }); @@ -894,7 +892,7 @@ pub(super) fn check_specialization_validity<'tcx>( } } -pub(super) fn check_impl_items_against_trait<'tcx>( +fn check_impl_items_against_trait<'tcx>( tcx: TyCtxt<'tcx>, full_impl_span: Span, impl_id: LocalDefId, @@ -926,174 +924,82 @@ pub(super) fn check_impl_items_against_trait<'tcx>( } } - // Locate trait definition and items let trait_def = tcx.trait_def(impl_trait_ref.def_id); - let impl_items = impl_item_refs.iter().map(|iiref| tcx.hir().impl_item(iiref.id)); - let associated_items = tcx.associated_items(impl_trait_ref.def_id); - - // Check existing impl methods to see if they are both present in trait - // and compatible with trait signature - for impl_item in impl_items { - let ty_impl_item = tcx.associated_item(impl_item.def_id); - - let mut items = - associated_items.filter_by_name(tcx, ty_impl_item.ident, impl_trait_ref.def_id); - - let (compatible_kind, ty_trait_item) = if let Some(ty_trait_item) = items.next() { - let is_compatible = |ty: &&ty::AssocItem| match (ty.kind, &impl_item.kind) { - (ty::AssocKind::Const, hir::ImplItemKind::Const(..)) => true, - (ty::AssocKind::Fn, hir::ImplItemKind::Fn(..)) => true, - (ty::AssocKind::Type, hir::ImplItemKind::TyAlias(..)) => true, - _ => false, - }; - - // If we don't have a compatible item, we'll use the first one whose name matches - // to report an error. - let mut compatible_kind = is_compatible(&ty_trait_item); - let mut trait_item = ty_trait_item; - if !compatible_kind { - if let Some(ty_trait_item) = items.find(is_compatible) { - compatible_kind = true; - trait_item = ty_trait_item; - } - } - - (compatible_kind, trait_item) + for impl_item in impl_item_refs { + let ty_impl_item = tcx.associated_item(impl_item.id.def_id); + let ty_trait_item = if let Some(trait_item_id) = ty_impl_item.trait_item_def_id { + tcx.associated_item(trait_item_id) } else { + // Checked in `associated_item`. + tcx.sess.delay_span_bug(impl_item.span, "missing associated item in trait"); continue; }; - - if compatible_kind { - match impl_item.kind { - hir::ImplItemKind::Const(..) => { - // Find associated const definition. - compare_const_impl( - tcx, - &ty_impl_item, - impl_item.span, - &ty_trait_item, - impl_trait_ref, - ); - } - hir::ImplItemKind::Fn(..) => { - let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id); - compare_impl_method( - tcx, - &ty_impl_item, - impl_item.span, - &ty_trait_item, - impl_trait_ref, - opt_trait_span, - ); - } - hir::ImplItemKind::TyAlias(impl_ty) => { - let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id); - compare_ty_impl( - tcx, - &ty_impl_item, - impl_ty.span, - &ty_trait_item, - impl_trait_ref, - opt_trait_span, - ); - } + let impl_item_full = tcx.hir().impl_item(impl_item.id); + match impl_item_full.kind { + hir::ImplItemKind::Const(..) => { + // Find associated const definition. + compare_const_impl( + tcx, + &ty_impl_item, + impl_item.span, + &ty_trait_item, + impl_trait_ref, + ); + } + hir::ImplItemKind::Fn(..) => { + let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id); + compare_impl_method( + tcx, + &ty_impl_item, + impl_item.span, + &ty_trait_item, + impl_trait_ref, + opt_trait_span, + ); + } + hir::ImplItemKind::TyAlias(impl_ty) => { + let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id); + compare_ty_impl( + tcx, + &ty_impl_item, + impl_ty.span, + &ty_trait_item, + impl_trait_ref, + opt_trait_span, + ); } - - check_specialization_validity( - tcx, - trait_def, - &ty_trait_item, - impl_id.to_def_id(), - impl_item, - ); - } else { - report_mismatch_error( - tcx, - ty_trait_item.def_id, - impl_trait_ref, - impl_item, - &ty_impl_item, - ); } + + check_specialization_validity( + tcx, + trait_def, + &ty_trait_item, + impl_id.to_def_id(), + impl_item, + ); } if let Ok(ancestors) = trait_def.ancestors(tcx, impl_id.to_def_id()) { - let impl_span = tcx.sess.source_map().guess_head_span(full_impl_span); - // Check for missing items from trait let mut missing_items = Vec::new(); - for trait_item in tcx.associated_items(impl_trait_ref.def_id).in_definition_order() { + for &trait_item_id in tcx.associated_item_def_ids(impl_trait_ref.def_id) { let is_implemented = ancestors - .leaf_def(tcx, trait_item.ident, trait_item.kind) - .map(|node_item| !node_item.defining_node.is_from_trait()) - .unwrap_or(false); + .leaf_def(tcx, trait_item_id) + .map_or(false, |node_item| node_item.item.defaultness.has_value()); if !is_implemented && tcx.impl_defaultness(impl_id).is_final() { - if !trait_item.defaultness.has_value() { - missing_items.push(*trait_item); - } + missing_items.push(tcx.associated_item(trait_item_id)); } } if !missing_items.is_empty() { + let impl_span = tcx.sess.source_map().guess_head_span(full_impl_span); missing_items_err(tcx, impl_span, &missing_items, full_impl_span); } } } -#[inline(never)] -#[cold] -fn report_mismatch_error<'tcx>( - tcx: TyCtxt<'tcx>, - trait_item_def_id: DefId, - impl_trait_ref: ty::TraitRef<'tcx>, - impl_item: &hir::ImplItem<'_>, - ty_impl_item: &ty::AssocItem, -) { - let mut err = match impl_item.kind { - hir::ImplItemKind::Const(..) => { - // Find associated const definition. - struct_span_err!( - tcx.sess, - impl_item.span, - E0323, - "item `{}` is an associated const, which doesn't match its trait `{}`", - ty_impl_item.ident, - impl_trait_ref.print_only_trait_path() - ) - } - - hir::ImplItemKind::Fn(..) => { - struct_span_err!( - tcx.sess, - impl_item.span, - E0324, - "item `{}` is an associated method, which doesn't match its trait `{}`", - ty_impl_item.ident, - impl_trait_ref.print_only_trait_path() - ) - } - - hir::ImplItemKind::TyAlias(_) => { - struct_span_err!( - tcx.sess, - impl_item.span, - E0325, - "item `{}` is an associated type, which doesn't match its trait `{}`", - ty_impl_item.ident, - impl_trait_ref.print_only_trait_path() - ) - } - }; - - err.span_label(impl_item.span, "does not match trait"); - if let Some(trait_span) = tcx.hir().span_if_local(trait_item_def_id) { - err.span_label(trait_span, "item in trait"); - } - err.emit(); -} - /// Checks whether a type can be represented in memory. In particular, it /// identifies types that contain themselves without indirection through a /// pointer, which would mean their size is unbounded. @@ -1271,7 +1177,7 @@ pub(super) fn check_packed_inner( if let ty::Adt(def, _) = field.ty(tcx, substs).kind() { if !stack.contains(&def.did) { if let Some(mut defs) = check_packed_inner(tcx, def.did, stack) { - defs.push((def.did, field.ident.span)); + defs.push((def.did, field.ident(tcx).span)); return Some(defs); } } diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs index 6192c77d6c6..01221e5dfa9 100644 --- a/compiler/rustc_typeck/src/check/coercion.rs +++ b/compiler/rustc_typeck/src/check/coercion.rs @@ -1667,10 +1667,10 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { ], Applicability::MachineApplicable, ); - let sugg = vec![sp, cause.span] + let sugg = [sp, cause.span] .into_iter() .flat_map(|sp| { - vec![ + [ (sp.shrink_to_lo(), "Box::new(".to_string()), (sp.shrink_to_hi(), ")".to_string()), ] diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index b7e276b6965..c351a9f7040 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -31,9 +31,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { error: TypeError<'tcx>, ) { self.annotate_expected_due_to_let_ty(err, expr, error); - self.suggest_box_deref(err, expr, expected, expr_ty); - self.suggest_compatible_variants(err, expr, expected, expr_ty); self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr); + self.suggest_compatible_variants(err, expr, expected, expr_ty); if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) { return; } @@ -259,23 +258,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn suggest_box_deref( - &self, - err: &mut DiagnosticBuilder<'_>, - expr: &hir::Expr<'_>, - expected: Ty<'tcx>, - expr_ty: Ty<'tcx>, - ) { - if expr_ty.is_box() && expr_ty.boxed_ty() == expected { - err.span_suggestion_verbose( - expr.span.shrink_to_lo(), - "try dereferencing the `Box`", - "*".to_string(), - Applicability::MachineApplicable, - ); - } - } - /// If the expected type is an enum (Issue #55250) with any variants whose /// sole field is of the found type, suggest such variants. (Issue #42764) fn suggest_compatible_variants( @@ -857,14 +839,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable, false, )); - } else if self.infcx.type_is_copy_modulo_regions( - self.param_env, - expected, - sp, - ) { - // For this suggestion to make sense, the type would need to be `Copy`. + } + + // For this suggestion to make sense, the type would need to be `Copy`, + // or we have to be moving out of a `Box<T>` + if self.infcx.type_is_copy_modulo_regions(self.param_env, expected, sp) + || checked_ty.is_box() + { if let Ok(code) = sm.span_to_snippet(expr.span) { - let message = if checked_ty.is_region_ptr() { + let message = if checked_ty.is_box() { + "consider unboxing the value" + } else if checked_ty.is_region_ptr() { "consider dereferencing the borrow" } else { "consider dereferencing the type" diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 621938c9b78..14180526d84 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -1376,7 +1376,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .fields .iter() .enumerate() - .map(|(i, field)| (field.ident.normalize_to_macros_2_0(), (i, field))) + .map(|(i, field)| (field.ident(tcx).normalize_to_macros_2_0(), (i, field))) .collect::<FxHashMap<_, _>>(); let mut seen_fields = FxHashMap::default(); @@ -1457,7 +1457,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr_span, self.field_ty(base_expr.span, f, base_subs), ); - let ident = self.tcx.adjust_ident(f.ident, variant.def_id); + let ident = self + .tcx + .adjust_ident(f.ident(self.tcx), variant.def_id); if let Some(_) = remaining_fields.remove(&ident) { let target_ty = self.field_ty(base_expr.span, f, substs); @@ -1475,10 +1477,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &cause, target_ty, fru_ty, - FieldMisMatch( - variant.ident.name, - ident.name, - ), + FieldMisMatch(variant.name, ident.name), ) .emit(), } @@ -1665,7 +1664,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "{} `{}::{}` has no field named `{}`", kind_name, actual, - variant.ident, + variant.name, field.ident ), _ => struct_span_err!( @@ -1680,15 +1679,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, ty, ); + + let variant_ident_span = self.tcx.def_ident_span(variant.def_id).unwrap(); match variant.ctor_kind { CtorKind::Fn => match ty.kind() { ty::Adt(adt, ..) if adt.is_enum() => { err.span_label( - variant.ident.span, + variant_ident_span, format!( "`{adt}::{variant}` defined here", adt = ty, - variant = variant.ident, + variant = variant.name, ), ); err.span_label(field.ident.span, "field does not exist"); @@ -1697,18 +1698,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &format!( "`{adt}::{variant}` is a tuple {kind_name}, use the appropriate syntax", adt = ty, - variant = variant.ident, + variant = variant.name, ), format!( "{adt}::{variant}(/* fields */)", adt = ty, - variant = variant.ident, + variant = variant.name, ), Applicability::HasPlaceholders, ); } _ => { - err.span_label(variant.ident.span, format!("`{adt}` defined here", adt = ty)); + err.span_label(variant_ident_span, format!("`{adt}` defined here", adt = ty)); err.span_label(field.ident.span, "field does not exist"); err.span_suggestion_verbose( expr_span, @@ -1740,7 +1741,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if adt.is_enum() { err.span_label( field.ident.span, - format!("`{}::{}` does not have this field", ty, variant.ident), + format!("`{}::{}` does not have this field", ty, variant.name), ); } else { err.span_label( @@ -1775,12 +1776,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .iter() .filter_map(|field| { // ignore already set fields and private fields from non-local crates - if skip.iter().any(|&x| x == field.ident.name) + if skip.iter().any(|&x| x == field.name) || (!variant.def_id.is_local() && !field.vis.is_public()) { None } else { - Some(field.ident.name) + Some(field.name) } }) .collect::<Vec<Symbol>>(); @@ -1795,11 +1796,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .filter(|field| { let def_scope = self .tcx - .adjust_ident_and_get_scope(field.ident, variant.def_id, self.body_id) + .adjust_ident_and_get_scope(field.ident(self.tcx), variant.def_id, self.body_id) .1; field.vis.is_accessible_from(def_scope, self.tcx) }) - .map(|field| field.ident.name) + .map(|field| field.name) .collect() } @@ -1834,8 +1835,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (ident, def_scope) = self.tcx.adjust_ident_and_get_scope(field, base_def.did, self.body_id); let fields = &base_def.non_enum_variant().fields; - if let Some(index) = - fields.iter().position(|f| f.ident.normalize_to_macros_2_0() == ident) + if let Some(index) = fields + .iter() + .position(|f| f.ident(self.tcx).normalize_to_macros_2_0() == ident) { let field = &fields[index]; let field_ty = self.field_ty(expr.span, field, substs); @@ -1916,7 +1918,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let ty::Adt(def, _) = output_ty.kind() { // no field access on enum type if !def.is_enum() { - if def.non_enum_variant().fields.iter().any(|field| field.ident == field_ident) { + if def + .non_enum_variant() + .fields + .iter() + .any(|field| field.ident(self.tcx) == field_ident) + { add_label = false; err.span_label( field_ident.span, @@ -2075,7 +2082,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .unwrap() .fields .iter() - .any(|f| f.ident == field) + .any(|f| f.ident(self.tcx) == field) { if let Some(dot_loc) = expr_snippet.rfind('.') { found = true; @@ -2262,7 +2269,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, candidate_field, field_path ); - if candidate_field.ident == target_field { + if candidate_field.ident(self.tcx) == target_field { Some(field_path) } else if field_path.len() > 3 { // For compile-time reasons and to avoid infinite recursion we only check for fields @@ -2271,11 +2278,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { // recursively search fields of `candidate_field` if it's a ty::Adt - field_path.push(candidate_field.ident.normalize_to_macros_2_0()); + field_path.push(candidate_field.ident(self.tcx).normalize_to_macros_2_0()); let field_ty = candidate_field.ty(self.tcx, subst); if let Some((nested_fields, subst)) = self.get_field_candidates(span, &field_ty) { for field in nested_fields.iter() { - let ident = field.ident.normalize_to_macros_2_0(); + let ident = field.ident(self.tcx).normalize_to_macros_2_0(); if ident == target_field { return Some(field_path); } else { diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs index 6c7d3a0c9c0..e8a0cc946b5 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs @@ -9,7 +9,7 @@ use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind}; use rustc_hir::lang_items::LangItem; use rustc_hir::{Expr, ExprKind, ItemKind, Node, Path, QPath, Stmt, StmtKind, TyKind}; -use rustc_infer::infer; +use rustc_infer::infer::{self, TyCtxtInferExt}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Binder, Ty}; use rustc_span::symbol::{kw, sym}; @@ -608,6 +608,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let bound_vars = self.tcx.late_bound_vars(fn_id); let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars)); let ty = self.normalize_associated_types_in(expr.span, ty); + let ty = match self.tcx.asyncness(fn_id.owner) { + hir::IsAsync::Async => self.tcx.infer_ctxt().enter(|infcx| { + infcx.get_impl_future_output_ty(ty).unwrap_or_else(|| { + span_bug!( + fn_decl.output.span(), + "failed to get output type of async function" + ) + }) + }), + hir::IsAsync::NotAsync => ty, + }; if self.can_coerce(found, ty) { err.multipart_suggestion( "you might have meant to return this value", diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index f7f4c52c2a1..5057be70c48 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -359,6 +359,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (obligation, substs) = self.obligation_for_method(span, trait_def_id, self_ty, opt_input_types); + debug!(?obligation); + // Now we want to know if this can be matched if !self.predicate_may_hold(&obligation) { debug!("--> Cannot match obligation"); @@ -482,7 +484,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let variant_def = adt_def .variants .iter() - .find(|vd| tcx.hygienic_eq(method_name, vd.ident, adt_def.did)); + .find(|vd| tcx.hygienic_eq(method_name, vd.ident(tcx), adt_def.did)); if let Some(variant_def) = variant_def { // Braced variants generate unusable names in value namespace (reserved for // possible future use), so variants resolved as associated items may refer to diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index 7f9c75c7fee..de83e45329b 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -15,7 +15,7 @@ use rustc_middle::ty::print::with_crate_prefix; use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeFoldable}; use rustc_span::lev_distance; use rustc_span::symbol::{kw, sym, Ident}; -use rustc_span::{source_map, FileName, MultiSpan, Span, Symbol}; +use rustc_span::{source_map, FileName, MultiSpan, Span}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{ FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, @@ -997,7 +997,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if unsatisfied_predicates.is_empty() && actual.is_enum() { let adt_def = actual.ty_adt_def().expect("enum is not an ADT"); if let Some(suggestion) = lev_distance::find_best_match_for_name( - &adt_def.variants.iter().map(|s| s.ident.name).collect::<Vec<_>>(), + &adt_def.variants.iter().map(|s| s.name).collect::<Vec<_>>(), item_name.name, None, ) { @@ -1321,7 +1321,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if Some(*parent_did) != self.tcx.parent(*trait_did) && self .tcx - .item_children(*parent_did) + .module_children(*parent_did) .iter() .filter(|child| child.res.opt_def_id() == Some(*trait_did)) .all(|child| child.ident.name == kw::Underscore) @@ -1524,8 +1524,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Explicitly ignore the `Pin::as_ref()` method as `Pin` does not // implement the `AsRef` trait. let skip = skippable.contains(&did) - || (("Pin::new" == *pre) - && (Symbol::intern("as_ref") == item_name.name)); + || (("Pin::new" == *pre) && (sym::as_ref == item_name.name)); // Make sure the method is defined for the *actual* receiver: we don't // want to treat `Box<Self>` as a receiver if it only works because of // an autoderef to `&self` diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index a9e6b1caff0..d576154ff90 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -566,7 +566,7 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId, span: S fn report_forbidden_specialization( tcx: TyCtxt<'_>, - impl_item: &hir::ImplItem<'_>, + impl_item: &hir::ImplItemRef, parent_impl: DefId, ) { let mut err = struct_span_err!( @@ -598,7 +598,7 @@ fn report_forbidden_specialization( fn missing_items_err( tcx: TyCtxt<'_>, impl_span: Span, - missing_items: &[ty::AssocItem], + missing_items: &[&ty::AssocItem], full_impl_span: Span, ) { let missing_items_msg = missing_items diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index ec06e0b1126..17b97d4cad1 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -1029,7 +1029,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let field_def_spans = if fields.is_empty() { vec![res_span] } else { - fields.iter().map(|f| f.ident.span).collect() + fields.iter().map(|f| f.ident(self.tcx).span).collect() }; let last_field_def_span = *field_def_spans.last().unwrap(); @@ -1231,7 +1231,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .fields .iter() .enumerate() - .map(|(i, field)| (field.ident.normalize_to_macros_2_0(), (i, field))) + .map(|(i, field)| (field.ident(self.tcx).normalize_to_macros_2_0(), (i, field))) .collect::<FxHashMap<_, _>>(); // Keep track of which fields have already appeared in the pattern. @@ -1272,7 +1272,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut unmentioned_fields = variant .fields .iter() - .map(|field| (field, field.ident.normalize_to_macros_2_0())) + .map(|field| (field, field.ident(self.tcx).normalize_to_macros_2_0())) .filter(|(_, ident)| !used_fields.contains_key(ident)) .collect::<Vec<_>>(); @@ -1579,7 +1579,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fields: &[hir::PatField<'_>], variant: &VariantDef, ) -> String { - let variant_field_idents = variant.fields.iter().map(|f| f.ident).collect::<Vec<Ident>>(); + let variant_field_idents = + variant.fields.iter().map(|f| f.ident(self.tcx)).collect::<Vec<Ident>>(); fields .iter() .map(|field| { diff --git a/compiler/rustc_typeck/src/check/regionck.rs b/compiler/rustc_typeck/src/check/regionck.rs index 1b42edc83be..0c2f4073350 100644 --- a/compiler/rustc_typeck/src/check/regionck.rs +++ b/compiler/rustc_typeck/src/check/regionck.rs @@ -859,15 +859,15 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { self.sub_regions( infer::ReborrowUpvar(span, upvar_id), borrow_region, - upvar_borrow.region, + captured_place.region.unwrap(), ); - if let ty::ImmBorrow = upvar_borrow.kind { + if let ty::ImmBorrow = upvar_borrow { debug!("link_upvar_region: capture by shared ref"); } else { all_captures_are_imm_borrow = false; } } - ty::UpvarCapture::ByValue(_) => { + ty::UpvarCapture::ByValue => { all_captures_are_imm_borrow = false; } } diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index ffd7d29bbbb..8d3c70b0573 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -33,7 +33,6 @@ use super::FnCtxt; use crate::expr_use_visitor as euv; -use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -72,7 +71,7 @@ enum PlaceAncestryRelation { /// Intermediate format to store a captured `Place` and associated `ty::CaptureInfo` /// during capture analysis. Information in this map feeds into the minimum capture /// analysis pass. -type InferredCaptureInformation<'tcx> = FxIndexMap<Place<'tcx>, ty::CaptureInfo<'tcx>>; +type InferredCaptureInformation<'tcx> = Vec<(Place<'tcx>, ty::CaptureInfo)>; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn closure_analyze(&self, body: &'tcx hir::Body<'tcx>) { @@ -207,8 +206,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { assert_eq!(body_owner_def_id.to_def_id(), closure_def_id); let mut delegate = InferBorrowKind { fcx: self, - closure_def_id, - closure_span: span, + closure_def_id: local_def_id, capture_information: Default::default(), fake_reads: Default::default(), }; @@ -231,7 +229,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (capture_information, closure_kind, origin) = self .process_collected_capture_information(capture_clause, delegate.capture_information); - self.compute_min_captures(closure_def_id, capture_information); + self.compute_min_captures(closure_def_id, capture_information, span); let closure_hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id); @@ -252,21 +250,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("seed place {:?}", place); - let upvar_id = ty::UpvarId::new(*var_hir_id, local_def_id); - let capture_kind = - self.init_capture_kind_for_place(&place, capture_clause, upvar_id, span); + let capture_kind = self.init_capture_kind_for_place(&place, capture_clause); let fake_info = ty::CaptureInfo { capture_kind_expr_id: None, path_expr_id: None, capture_kind, }; - capture_information.insert(place, fake_info); + capture_information.push((place, fake_info)); } } // This will update the min captures based on this new fake information. - self.compute_min_captures(closure_def_id, capture_information); + self.compute_min_captures(closure_def_id, capture_information, span); } let before_feature_tys = self.final_upvar_tys(closure_def_id); @@ -362,7 +358,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { captured_place.place, upvar_ty, capture, captured_place.mutability, ); - apply_capture_kind_on_capture_ty(self.tcx, upvar_ty, capture) + apply_capture_kind_on_capture_ty(self.tcx, upvar_ty, capture, captured_place.region) }) .collect() } @@ -387,77 +383,68 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { capture_clause: hir::CaptureBy, capture_information: InferredCaptureInformation<'tcx>, ) -> (InferredCaptureInformation<'tcx>, ty::ClosureKind, Option<(Span, Place<'tcx>)>) { - let mut processed: InferredCaptureInformation<'tcx> = Default::default(); - let mut closure_kind = ty::ClosureKind::LATTICE_BOTTOM; let mut origin: Option<(Span, Place<'tcx>)> = None; - for (place, mut capture_info) in capture_information { - // Apply rules for safety before inferring closure kind - let (place, capture_kind) = - restrict_capture_precision(place, capture_info.capture_kind); - capture_info.capture_kind = capture_kind; + let processed = capture_information + .into_iter() + .map(|(place, mut capture_info)| { + // Apply rules for safety before inferring closure kind + let (place, capture_kind) = + restrict_capture_precision(place, capture_info.capture_kind); - let (place, capture_kind) = - truncate_capture_for_optimization(place, capture_info.capture_kind); - capture_info.capture_kind = capture_kind; + let (place, capture_kind) = truncate_capture_for_optimization(place, capture_kind); - let usage_span = if let Some(usage_expr) = capture_info.path_expr_id { - self.tcx.hir().span(usage_expr) - } else { - unreachable!() - }; + let usage_span = if let Some(usage_expr) = capture_info.path_expr_id { + self.tcx.hir().span(usage_expr) + } else { + unreachable!() + }; - let updated = match capture_info.capture_kind { - ty::UpvarCapture::ByValue(..) => match closure_kind { - ty::ClosureKind::Fn | ty::ClosureKind::FnMut => { - (ty::ClosureKind::FnOnce, Some((usage_span, place.clone()))) - } - // If closure is already FnOnce, don't update - ty::ClosureKind::FnOnce => (closure_kind, origin), - }, + let updated = match capture_kind { + ty::UpvarCapture::ByValue => match closure_kind { + ty::ClosureKind::Fn | ty::ClosureKind::FnMut => { + (ty::ClosureKind::FnOnce, Some((usage_span, place.clone()))) + } + // If closure is already FnOnce, don't update + ty::ClosureKind::FnOnce => (closure_kind, origin.take()), + }, - ty::UpvarCapture::ByRef(ty::UpvarBorrow { - kind: ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow, - .. - }) => { - match closure_kind { - ty::ClosureKind::Fn => { - (ty::ClosureKind::FnMut, Some((usage_span, place.clone()))) + ty::UpvarCapture::ByRef( + ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow, + ) => { + match closure_kind { + ty::ClosureKind::Fn => { + (ty::ClosureKind::FnMut, Some((usage_span, place.clone()))) + } + // Don't update the origin + ty::ClosureKind::FnMut | ty::ClosureKind::FnOnce => { + (closure_kind, origin.take()) + } } - // Don't update the origin - ty::ClosureKind::FnMut | ty::ClosureKind::FnOnce => (closure_kind, origin), } - } - _ => (closure_kind, origin), - }; - - closure_kind = updated.0; - origin = updated.1; + _ => (closure_kind, origin.take()), + }; - let (place, capture_kind) = match capture_clause { - hir::CaptureBy::Value => adjust_for_move_closure(place, capture_info.capture_kind), - hir::CaptureBy::Ref => { - adjust_for_non_move_closure(place, capture_info.capture_kind) - } - }; + closure_kind = updated.0; + origin = updated.1; - // This restriction needs to be applied after we have handled adjustments for `move` - // closures. We want to make sure any adjustment that might make us move the place into - // the closure gets handled. - let (place, capture_kind) = - restrict_precision_for_drop_types(self, place, capture_kind, usage_span); + let (place, capture_kind) = match capture_clause { + hir::CaptureBy::Value => adjust_for_move_closure(place, capture_kind), + hir::CaptureBy::Ref => adjust_for_non_move_closure(place, capture_kind), + }; - capture_info.capture_kind = capture_kind; + // This restriction needs to be applied after we have handled adjustments for `move` + // closures. We want to make sure any adjustment that might make us move the place into + // the closure gets handled. + let (place, capture_kind) = + restrict_precision_for_drop_types(self, place, capture_kind, usage_span); - let capture_info = if let Some(existing) = processed.get(&place) { - determine_capture_info(*existing, capture_info) - } else { - capture_info - }; - processed.insert(place, capture_info); - } + capture_info.capture_kind = capture_kind; + (place, capture_info) + }) + .collect(); (processed, closure_kind, origin) } @@ -535,6 +522,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, closure_def_id: DefId, capture_information: InferredCaptureInformation<'tcx>, + closure_span: Span, ) { if capture_information.is_empty() { return; @@ -554,8 +542,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) { None => { let mutability = self.determine_capture_mutability(&typeck_results, &place); - let min_cap_list = - vec![ty::CapturedPlace { place, info: capture_info, mutability }]; + let min_cap_list = vec![ty::CapturedPlace { + place, + info: capture_info, + mutability, + region: None, + }]; root_var_min_capture_list.insert(var_hir_id, min_cap_list); continue; } @@ -608,8 +600,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !descendant_found { for possible_ancestor in min_cap_list.iter_mut() { match determine_place_ancestry_relation(&place, &possible_ancestor.place) { + PlaceAncestryRelation::SamePlace => { + ancestor_found = true; + possible_ancestor.info = determine_capture_info( + possible_ancestor.info, + updated_capture_info, + ); + + // Only one related place will be in the list. + break; + } // current place is descendant of possible_ancestor - PlaceAncestryRelation::Descendant | PlaceAncestryRelation::SamePlace => { + PlaceAncestryRelation::Descendant => { ancestor_found = true; let backup_path_expr_id = possible_ancestor.info.path_expr_id; @@ -629,7 +631,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // we need to keep the ancestor's `path_expr_id` possible_ancestor.info.path_expr_id = backup_path_expr_id; - // Only one ancestor of the current place will be in the list. + // Only one related place will be in the list. break; } _ => {} @@ -640,12 +642,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Only need to insert when we don't have an ancestor in the existing min capture list if !ancestor_found { let mutability = self.determine_capture_mutability(&typeck_results, &place); - let captured_place = - ty::CapturedPlace { place, info: updated_capture_info, mutability }; + let captured_place = ty::CapturedPlace { + place, + info: updated_capture_info, + mutability, + region: None, + }; min_cap_list.push(captured_place); } } + // For each capture that is determined to be captured by ref, add region info. + for (_, captures) in &mut root_var_min_capture_list { + for capture in captures { + match capture.info.capture_kind { + ty::UpvarCapture::ByRef(_) => { + let PlaceBase::Upvar(upvar_id) = capture.place.base else { bug!("expected upvar") }; + let origin = UpvarRegion(upvar_id, closure_span); + let upvar_region = self.next_region_var(origin); + capture.region = Some(upvar_region); + } + _ => (), + } + } + } + debug!( "For closure={:?}, min_captures before sorting={:?}", closure_def_id, root_var_min_capture_list @@ -947,7 +968,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { max_capture_info = determine_capture_info(max_capture_info, capture.info); } - apply_capture_kind_on_capture_ty(self.tcx, ty, max_capture_info.capture_kind) + apply_capture_kind_on_capture_ty( + self.tcx, + ty, + max_capture_info.capture_kind, + Some(&ty::ReErased), + ) } }; @@ -977,6 +1003,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx, capture.place.ty(), capture.info.capture_kind, + Some(&ty::ReErased), ); // Checks if a capture implements any of the auto traits @@ -1086,7 +1113,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for captured_place in root_var_min_capture_list.iter() { match captured_place.info.capture_kind { // Only care about captures that are moved into the closure - ty::UpvarCapture::ByValue(..) => { + ty::UpvarCapture::ByValue => { projections_list.push(captured_place.place.projections.as_slice()); diagnostics_info.insert(UpvarMigrationInfo::CapturingPrecise { source_expr: captured_place.info.path_expr_id, @@ -1470,9 +1497,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, place: &Place<'tcx>, capture_clause: hir::CaptureBy, - upvar_id: ty::UpvarId, - closure_span: Span, - ) -> ty::UpvarCapture<'tcx> { + ) -> ty::UpvarCapture { match capture_clause { // In case of a move closure if the data is accessed through a reference we // want to capture by ref to allow precise capture using reborrows. @@ -1481,14 +1506,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // at the first Deref in `adjust_upvar_borrow_kind_for_consume` and then moved into // the closure. hir::CaptureBy::Value if !place.deref_tys().any(ty::TyS::is_ref) => { - ty::UpvarCapture::ByValue(None) - } - hir::CaptureBy::Value | hir::CaptureBy::Ref => { - let origin = UpvarRegion(upvar_id, closure_span); - let upvar_region = self.next_region_var(origin); - let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow, region: upvar_region }; - ty::UpvarCapture::ByRef(upvar_borrow) + ty::UpvarCapture::ByValue } + hir::CaptureBy::Value | hir::CaptureBy::Ref => ty::UpvarCapture::ByRef(ty::ImmBorrow), } } @@ -1513,7 +1533,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn log_capture_analysis_first_pass( &self, closure_def_id: rustc_hir::def_id::DefId, - capture_information: &FxIndexMap<Place<'tcx>, ty::CaptureInfo<'tcx>>, + capture_information: &InferredCaptureInformation<'tcx>, closure_span: Span, ) { if self.should_log_capture_analysis(closure_def_id) { @@ -1629,9 +1649,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn restrict_repr_packed_field_ref_capture<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, - place: &Place<'tcx>, - mut curr_borrow_kind: ty::UpvarCapture<'tcx>, -) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) { + mut place: Place<'tcx>, + mut curr_borrow_kind: ty::UpvarCapture, +) -> (Place<'tcx>, ty::UpvarCapture) { let pos = place.projections.iter().enumerate().position(|(i, p)| { let ty = place.ty_before_projection(i); @@ -1662,8 +1682,6 @@ fn restrict_repr_packed_field_ref_capture<'tcx>( } }); - let mut place = place.clone(); - if let Some(pos) = pos { truncate_place_to_len_and_update_capture_kind(&mut place, &mut curr_borrow_kind, pos); } @@ -1675,12 +1693,14 @@ fn restrict_repr_packed_field_ref_capture<'tcx>( fn apply_capture_kind_on_capture_ty<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, - capture_kind: UpvarCapture<'tcx>, + capture_kind: UpvarCapture, + region: Option<ty::Region<'tcx>>, ) -> Ty<'tcx> { match capture_kind { - ty::UpvarCapture::ByValue(_) => ty, - ty::UpvarCapture::ByRef(borrow) => tcx - .mk_ref(borrow.region, ty::TypeAndMut { ty: ty, mutbl: borrow.kind.to_mutbl_lossy() }), + ty::UpvarCapture::ByValue => ty, + ty::UpvarCapture::ByRef(kind) => { + tcx.mk_ref(region.unwrap(), ty::TypeAndMut { ty: ty, mutbl: kind.to_mutbl_lossy() }) + } } } @@ -1708,9 +1728,7 @@ struct InferBorrowKind<'a, 'tcx> { fcx: &'a FnCtxt<'a, 'tcx>, // The def-id of the closure whose kind and upvar accesses are being inferred. - closure_def_id: DefId, - - closure_span: Span, + closure_def_id: LocalDefId, /// For each Place that is captured by the closure, we track the minimal kind of /// access we need (ref, ref mut, move, etc) and the expression that resulted in such access. @@ -1742,184 +1760,38 @@ struct InferBorrowKind<'a, 'tcx> { fake_reads: Vec<(Place<'tcx>, FakeReadCause, hir::HirId)>, } -impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { - #[instrument(skip(self), level = "debug")] - fn adjust_upvar_borrow_kind_for_consume( - &mut self, - place_with_id: &PlaceWithHirId<'tcx>, - diag_expr_id: hir::HirId, - ) { - let tcx = self.fcx.tcx; - let PlaceBase::Upvar(upvar_id) = place_with_id.place.base else { - return; - }; - - debug!(?upvar_id); - - let usage_span = tcx.hir().span(diag_expr_id); - - let capture_info = ty::CaptureInfo { - capture_kind_expr_id: Some(diag_expr_id), - path_expr_id: Some(diag_expr_id), - capture_kind: ty::UpvarCapture::ByValue(Some(usage_span)), - }; - - let curr_info = self.capture_information[&place_with_id.place]; - let updated_info = determine_capture_info(curr_info, capture_info); - - self.capture_information[&place_with_id.place] = updated_info; - } - - /// Indicates that `place_with_id` is being directly mutated (e.g., assigned - /// to). If the place is based on a by-ref upvar, this implies that - /// the upvar must be borrowed using an `&mut` borrow. - #[instrument(skip(self), level = "debug")] - fn adjust_upvar_borrow_kind_for_mut( - &mut self, - place_with_id: &PlaceWithHirId<'tcx>, - diag_expr_id: hir::HirId, - ) { - if let PlaceBase::Upvar(_) = place_with_id.place.base { - // Raw pointers don't inherit mutability - if place_with_id.place.deref_tys().any(ty::TyS::is_unsafe_ptr) { - return; - } - self.adjust_upvar_deref(place_with_id, diag_expr_id, ty::MutBorrow); - } - } - - #[instrument(skip(self), level = "debug")] - fn adjust_upvar_borrow_kind_for_unique( - &mut self, - place_with_id: &PlaceWithHirId<'tcx>, - diag_expr_id: hir::HirId, - ) { - if let PlaceBase::Upvar(_) = place_with_id.place.base { - if place_with_id.place.deref_tys().any(ty::TyS::is_unsafe_ptr) { - // Raw pointers don't inherit mutability. - return; - } - // for a borrowed pointer to be unique, its base must be unique - self.adjust_upvar_deref(place_with_id, diag_expr_id, ty::UniqueImmBorrow); - } - } - - fn adjust_upvar_deref( - &mut self, - place_with_id: &PlaceWithHirId<'tcx>, - diag_expr_id: hir::HirId, - borrow_kind: ty::BorrowKind, - ) { - assert!(match borrow_kind { - ty::MutBorrow => true, - ty::UniqueImmBorrow => true, - - // imm borrows never require adjusting any kinds, so we don't wind up here - ty::ImmBorrow => false, - }); - - // if this is an implicit deref of an - // upvar, then we need to modify the - // borrow_kind of the upvar to make sure it - // is inferred to mutable if necessary - self.adjust_upvar_borrow_kind(place_with_id, diag_expr_id, borrow_kind); - } - - /// We infer the borrow_kind with which to borrow upvars in a stack closure. - /// The borrow_kind basically follows a lattice of `imm < unique-imm < mut`, - /// moving from left to right as needed (but never right to left). - /// Here the argument `mutbl` is the borrow_kind that is required by - /// some particular use. - #[instrument(skip(self), level = "debug")] - fn adjust_upvar_borrow_kind( - &mut self, - place_with_id: &PlaceWithHirId<'tcx>, - diag_expr_id: hir::HirId, - kind: ty::BorrowKind, - ) { - let curr_capture_info = self.capture_information[&place_with_id.place]; - - debug!(?curr_capture_info); - - if let ty::UpvarCapture::ByValue(_) = curr_capture_info.capture_kind { - // It's already captured by value, we don't need to do anything here - return; - } else if let ty::UpvarCapture::ByRef(curr_upvar_borrow) = curr_capture_info.capture_kind { - // Use the same region as the current capture information - // Doesn't matter since only one of the UpvarBorrow will be used. - let new_upvar_borrow = ty::UpvarBorrow { kind, region: curr_upvar_borrow.region }; - - let capture_info = ty::CaptureInfo { - capture_kind_expr_id: Some(diag_expr_id), - path_expr_id: Some(diag_expr_id), - capture_kind: ty::UpvarCapture::ByRef(new_upvar_borrow), - }; - let updated_info = determine_capture_info(curr_capture_info, capture_info); - self.capture_information[&place_with_id.place] = updated_info; - }; - } - - #[instrument(skip(self, diag_expr_id), level = "debug")] - fn init_capture_info_for_place( - &mut self, - place_with_id: &PlaceWithHirId<'tcx>, - diag_expr_id: hir::HirId, - ) { - if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base { - assert_eq!(self.closure_def_id.expect_local(), upvar_id.closure_expr_id); - - // Initialize to ImmBorrow - // We will escalate the CaptureKind based on any uses we see or in `process_collected_capture_information`. - let origin = UpvarRegion(upvar_id, self.closure_span); - let upvar_region = self.fcx.next_region_var(origin); - let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow, region: upvar_region }; - let capture_kind = ty::UpvarCapture::ByRef(upvar_borrow); - - let expr_id = Some(diag_expr_id); - let capture_info = ty::CaptureInfo { - capture_kind_expr_id: expr_id, - path_expr_id: expr_id, - capture_kind, - }; - - debug!("Capturing new place {:?}, capture_info={:?}", place_with_id, capture_info); - - self.capture_information.insert(place_with_id.place.clone(), capture_info); - } else { - debug!("Not upvar"); - } - } -} - impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { fn fake_read(&mut self, place: Place<'tcx>, cause: FakeReadCause, diag_expr_id: hir::HirId) { - if let PlaceBase::Upvar(_) = place.base { - // We need to restrict Fake Read precision to avoid fake reading unsafe code, - // such as deref of a raw pointer. - let dummy_capture_kind = ty::UpvarCapture::ByRef(ty::UpvarBorrow { - kind: ty::BorrowKind::ImmBorrow, - region: &ty::ReErased, - }); + let PlaceBase::Upvar(_) = place.base else { return }; - let (place, _) = restrict_capture_precision(place, dummy_capture_kind); + // We need to restrict Fake Read precision to avoid fake reading unsafe code, + // such as deref of a raw pointer. + let dummy_capture_kind = ty::UpvarCapture::ByRef(ty::BorrowKind::ImmBorrow); - let (place, _) = restrict_repr_packed_field_ref_capture( - self.fcx.tcx, - self.fcx.param_env, - &place, - dummy_capture_kind, - ); - self.fake_reads.push((place, cause, diag_expr_id)); - } + let (place, _) = restrict_capture_precision(place, dummy_capture_kind); + + let (place, _) = restrict_repr_packed_field_ref_capture( + self.fcx.tcx, + self.fcx.param_env, + place, + dummy_capture_kind, + ); + self.fake_reads.push((place, cause, diag_expr_id)); } #[instrument(skip(self), level = "debug")] fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) { - if !self.capture_information.contains_key(&place_with_id.place) { - self.init_capture_info_for_place(place_with_id, diag_expr_id); - } + let PlaceBase::Upvar(upvar_id) = place_with_id.place.base else { return }; + assert_eq!(self.closure_def_id, upvar_id.closure_expr_id); - self.adjust_upvar_borrow_kind_for_consume(place_with_id, diag_expr_id); + self.capture_information.push(( + place_with_id.place.clone(), + ty::CaptureInfo { + capture_kind_expr_id: Some(diag_expr_id), + path_expr_id: Some(diag_expr_id), + capture_kind: ty::UpvarCapture::ByValue, + }, + )); } #[instrument(skip(self), level = "debug")] @@ -1929,40 +1801,35 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { diag_expr_id: hir::HirId, bk: ty::BorrowKind, ) { + let PlaceBase::Upvar(upvar_id) = place_with_id.place.base else { return }; + assert_eq!(self.closure_def_id, upvar_id.closure_expr_id); + // The region here will get discarded/ignored - let dummy_capture_kind = - ty::UpvarCapture::ByRef(ty::UpvarBorrow { kind: bk, region: &ty::ReErased }); + let capture_kind = ty::UpvarCapture::ByRef(bk); // We only want repr packed restriction to be applied to reading references into a packed // struct, and not when the data is being moved. Therefore we call this method here instead // of in `restrict_capture_precision`. - let (place, updated_kind) = restrict_repr_packed_field_ref_capture( + let (place, mut capture_kind) = restrict_repr_packed_field_ref_capture( self.fcx.tcx, self.fcx.param_env, - &place_with_id.place, - dummy_capture_kind, + place_with_id.place.clone(), + capture_kind, ); - let place_with_id = PlaceWithHirId { place, ..*place_with_id }; - - if !self.capture_information.contains_key(&place_with_id.place) { - self.init_capture_info_for_place(&place_with_id, diag_expr_id); + // Raw pointers don't inherit mutability + if place_with_id.place.deref_tys().any(ty::TyS::is_unsafe_ptr) { + capture_kind = ty::UpvarCapture::ByRef(ty::BorrowKind::ImmBorrow); } - match updated_kind { - ty::UpvarCapture::ByRef(ty::UpvarBorrow { kind, .. }) => match kind { - ty::ImmBorrow => {} - ty::UniqueImmBorrow => { - self.adjust_upvar_borrow_kind_for_unique(&place_with_id, diag_expr_id); - } - ty::MutBorrow => { - self.adjust_upvar_borrow_kind_for_mut(&place_with_id, diag_expr_id); - } + self.capture_information.push(( + place, + ty::CaptureInfo { + capture_kind_expr_id: Some(diag_expr_id), + path_expr_id: Some(diag_expr_id), + capture_kind, }, - - // Just truncating the place will never cause capture kind to be updated to ByValue - ty::UpvarCapture::ByValue(..) => unreachable!(), - } + )); } #[instrument(skip(self), level = "debug")] @@ -1975,12 +1842,12 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { fn restrict_precision_for_drop_types<'a, 'tcx>( fcx: &'a FnCtxt<'a, 'tcx>, mut place: Place<'tcx>, - mut curr_mode: ty::UpvarCapture<'tcx>, + mut curr_mode: ty::UpvarCapture, span: Span, -) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) { +) -> (Place<'tcx>, ty::UpvarCapture) { let is_copy_type = fcx.infcx.type_is_copy_modulo_regions(fcx.param_env, place.ty(), span); - if let (false, UpvarCapture::ByValue(..)) = (is_copy_type, curr_mode) { + if let (false, UpvarCapture::ByValue) = (is_copy_type, curr_mode) { for i in 0..place.projections.len() { match place.ty_before_projection(i).kind() { ty::Adt(def, _) if def.destructor(fcx.tcx).is_some() => { @@ -2001,8 +1868,8 @@ fn restrict_precision_for_drop_types<'a, 'tcx>( /// - No projections are applied on top of Union ADTs, since these require unsafe blocks. fn restrict_precision_for_unsafe<'tcx>( mut place: Place<'tcx>, - mut curr_mode: ty::UpvarCapture<'tcx>, -) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) { + mut curr_mode: ty::UpvarCapture, +) -> (Place<'tcx>, ty::UpvarCapture) { if place.base_ty.is_unsafe_ptr() { truncate_place_to_len_and_update_capture_kind(&mut place, &mut curr_mode, 0); } @@ -2034,8 +1901,8 @@ fn restrict_precision_for_unsafe<'tcx>( /// Returns the truncated place and updated cature mode. fn restrict_capture_precision<'tcx>( place: Place<'tcx>, - curr_mode: ty::UpvarCapture<'tcx>, -) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) { + curr_mode: ty::UpvarCapture, +) -> (Place<'tcx>, ty::UpvarCapture) { let (mut place, mut curr_mode) = restrict_precision_for_unsafe(place, curr_mode); if place.projections.is_empty() { @@ -2062,30 +1929,28 @@ fn restrict_capture_precision<'tcx>( /// Truncate deref of any reference. fn adjust_for_move_closure<'tcx>( mut place: Place<'tcx>, - mut kind: ty::UpvarCapture<'tcx>, -) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) { + mut kind: ty::UpvarCapture, +) -> (Place<'tcx>, ty::UpvarCapture) { let first_deref = place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref); if let Some(idx) = first_deref { truncate_place_to_len_and_update_capture_kind(&mut place, &mut kind, idx); } - // AMAN: I think we don't need the span inside the ByValue anymore - // we have more detailed span in CaptureInfo - (place, ty::UpvarCapture::ByValue(None)) + (place, ty::UpvarCapture::ByValue) } /// Adjust closure capture just that if taking ownership of data, only move data /// from enclosing stack frame. fn adjust_for_non_move_closure<'tcx>( mut place: Place<'tcx>, - mut kind: ty::UpvarCapture<'tcx>, -) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) { + mut kind: ty::UpvarCapture, +) -> (Place<'tcx>, ty::UpvarCapture) { let contains_deref = place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref); match kind { - ty::UpvarCapture::ByValue(..) => { + ty::UpvarCapture::ByValue => { if let Some(idx) = contains_deref { truncate_place_to_len_and_update_capture_kind(&mut place, &mut kind, idx); } @@ -2123,13 +1988,13 @@ fn construct_place_string<'tcx>(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String fn construct_capture_kind_reason_string<'tcx>( tcx: TyCtxt<'_>, place: &Place<'tcx>, - capture_info: &ty::CaptureInfo<'tcx>, + capture_info: &ty::CaptureInfo, ) -> String { let place_str = construct_place_string(tcx, place); let capture_kind_str = match capture_info.capture_kind { - ty::UpvarCapture::ByValue(_) => "ByValue".into(), - ty::UpvarCapture::ByRef(borrow) => format!("{:?}", borrow.kind), + ty::UpvarCapture::ByValue => "ByValue".into(), + ty::UpvarCapture::ByRef(kind) => format!("{:?}", kind), }; format!("{} captured as {} here", place_str, capture_kind_str) @@ -2144,13 +2009,13 @@ fn construct_path_string<'tcx>(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String { fn construct_capture_info_string<'tcx>( tcx: TyCtxt<'_>, place: &Place<'tcx>, - capture_info: &ty::CaptureInfo<'tcx>, + capture_info: &ty::CaptureInfo, ) -> String { let place_str = construct_place_string(tcx, place); let capture_kind_str = match capture_info.capture_kind { - ty::UpvarCapture::ByValue(_) => "ByValue".into(), - ty::UpvarCapture::ByRef(borrow) => format!("{:?}", borrow.kind), + ty::UpvarCapture::ByValue => "ByValue".into(), + ty::UpvarCapture::ByRef(kind) => format!("{:?}", kind), }; format!("{} -> {}", place_str, capture_kind_str) } @@ -2233,25 +2098,16 @@ fn migration_suggestion_for_2229( /// would've already handled `E1`, and have an existing capture_information for it. /// Calling `determine_capture_info(existing_info_e1, current_info_e2)` will return /// `existing_info_e1` in this case, allowing us to point to `E1` in case of diagnostics. -fn determine_capture_info<'tcx>( - capture_info_a: ty::CaptureInfo<'tcx>, - capture_info_b: ty::CaptureInfo<'tcx>, -) -> ty::CaptureInfo<'tcx> { +fn determine_capture_info( + capture_info_a: ty::CaptureInfo, + capture_info_b: ty::CaptureInfo, +) -> ty::CaptureInfo { // If the capture kind is equivalent then, we don't need to escalate and can compare the // expressions. let eq_capture_kind = match (capture_info_a.capture_kind, capture_info_b.capture_kind) { - (ty::UpvarCapture::ByValue(_), ty::UpvarCapture::ByValue(_)) => { - // We don't need to worry about the spans being ignored here. - // - // The expr_id in capture_info corresponds to the span that is stored within - // ByValue(span) and therefore it gets handled with priortizing based on - // expressions below. - true - } - (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => { - ref_a.kind == ref_b.kind - } - (ty::UpvarCapture::ByValue(_), _) | (ty::UpvarCapture::ByRef(_), _) => false, + (ty::UpvarCapture::ByValue, ty::UpvarCapture::ByValue) => true, + (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => ref_a == ref_b, + (ty::UpvarCapture::ByValue, _) | (ty::UpvarCapture::ByRef(_), _) => false, }; if eq_capture_kind { @@ -2263,10 +2119,10 @@ fn determine_capture_info<'tcx>( // We select the CaptureKind which ranks higher based the following priority order: // ByValue > MutBorrow > UniqueImmBorrow > ImmBorrow match (capture_info_a.capture_kind, capture_info_b.capture_kind) { - (ty::UpvarCapture::ByValue(_), _) => capture_info_a, - (_, ty::UpvarCapture::ByValue(_)) => capture_info_b, + (ty::UpvarCapture::ByValue, _) => capture_info_a, + (_, ty::UpvarCapture::ByValue) => capture_info_b, (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => { - match (ref_a.kind, ref_b.kind) { + match (ref_a, ref_b) { // Take LHS: (ty::UniqueImmBorrow | ty::MutBorrow, ty::ImmBorrow) | (ty::MutBorrow, ty::UniqueImmBorrow) => capture_info_a, @@ -2294,7 +2150,7 @@ fn determine_capture_info<'tcx>( /// contained `Deref` of `&mut`. fn truncate_place_to_len_and_update_capture_kind<'tcx>( place: &mut Place<'tcx>, - curr_mode: &mut ty::UpvarCapture<'tcx>, + curr_mode: &mut ty::UpvarCapture, len: usize, ) { let is_mut_ref = |ty: Ty<'_>| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Mut)); @@ -2304,22 +2160,19 @@ fn truncate_place_to_len_and_update_capture_kind<'tcx>( // Note that if the place contained Deref of a raw pointer it would've not been MutBorrow, so // we don't need to worry about that case here. match curr_mode { - ty::UpvarCapture::ByRef(ty::UpvarBorrow { kind: ty::BorrowKind::MutBorrow, region }) => { + ty::UpvarCapture::ByRef(ty::BorrowKind::MutBorrow) => { for i in len..place.projections.len() { if place.projections[i].kind == ProjectionKind::Deref && is_mut_ref(place.ty_before_projection(i)) { - *curr_mode = ty::UpvarCapture::ByRef(ty::UpvarBorrow { - kind: ty::BorrowKind::UniqueImmBorrow, - region, - }); + *curr_mode = ty::UpvarCapture::ByRef(ty::BorrowKind::UniqueImmBorrow); break; } } } ty::UpvarCapture::ByRef(..) => {} - ty::UpvarCapture::ByValue(..) => {} + ty::UpvarCapture::ByValue => {} } place.projections.truncate(len); @@ -2390,8 +2243,8 @@ fn determine_place_ancestry_relation<'tcx>( /// ``` fn truncate_capture_for_optimization<'tcx>( mut place: Place<'tcx>, - mut curr_mode: ty::UpvarCapture<'tcx>, -) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) { + mut curr_mode: ty::UpvarCapture, +) -> (Place<'tcx>, ty::UpvarCapture) { let is_shared_ref = |ty: Ty<'_>| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Not)); // Find the right-most deref (if any). All the projections that come after this diff --git a/compiler/rustc_typeck/src/coherence/builtin.rs b/compiler/rustc_typeck/src/coherence/builtin.rs index d5494c5a685..dff6b7b58a0 100644 --- a/compiler/rustc_typeck/src/coherence/builtin.rs +++ b/compiler/rustc_typeck/src/coherence/builtin.rs @@ -199,7 +199,7 @@ fn visit_implementation_of_dispatch_from_dyn<'tcx>(tcx: TyCtxt<'tcx>, impl_did: ) .note(&format!( "extra field `{}` of type `{}` is not allowed", - field.ident, ty_a, + field.name, ty_a, )) .emit(); @@ -235,7 +235,7 @@ fn visit_implementation_of_dispatch_from_dyn<'tcx>(tcx: TyCtxt<'tcx>, impl_did: .map(|field| { format!( "`{}` (`{}` to `{}`)", - field.ident, + field.name, field.ty(tcx, substs_a), field.ty(tcx, substs_b), ) @@ -479,7 +479,7 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn diff_fields .iter() .map(|&(i, a, b)| { - format!("`{}` (`{}` to `{}`)", fields[i].ident, a, b) + format!("`{}` (`{}` to `{}`)", fields[i].name, a, b) }) .collect::<Vec<_>>() .join(", ") diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 41c8a37a71a..3cccdb27448 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -21,7 +21,6 @@ use crate::constrained_generic_params as cgp; use crate::errors; use crate::middle::resolve_lifetime as rl; use rustc_ast as ast; -use rustc_ast::Attribute; use rustc_ast::{MetaItemKind, NestedMetaItem}; use rustc_attr::{list_contains_name, InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_data_structures::captures::Captures; @@ -995,7 +994,7 @@ fn convert_variant( seen_fields.insert(f.ident.normalize_to_macros_2_0(), f.span); } - ty::FieldDef { did: fid.to_def_id(), ident: f.ident, vis: tcx.visibility(fid) } + ty::FieldDef { did: fid.to_def_id(), name: f.ident.name, vis: tcx.visibility(fid) } }) .collect(); let recovered = match def { @@ -1003,7 +1002,7 @@ fn convert_variant( _ => false, }; ty::VariantDef::new( - ident, + ident.name, variant_did.map(LocalDefId::to_def_id), ctor_did.map(LocalDefId::to_def_id), discr, @@ -3120,8 +3119,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { if tcx.is_weak_lang_item(id) { codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; } - let check_name = |attr: &Attribute, sym| attr.has_name(sym); - if let Some(name) = weak_lang_items::link_name(check_name, attrs) { + if let Some(name) = weak_lang_items::link_name(attrs) { codegen_fn_attrs.export_name = Some(name); codegen_fn_attrs.link_name = Some(name); } @@ -3150,21 +3148,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { /// applied to the method prototype. fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool { if let Some(impl_item) = tcx.opt_associated_item(def_id) { - if let ty::AssocItemContainer::ImplContainer(impl_def_id) = impl_item.container { - if let Some(trait_def_id) = tcx.trait_id_of_impl(impl_def_id) { - if let Some(trait_item) = tcx - .associated_items(trait_def_id) - .filter_by_name_unhygienic(impl_item.ident.name) - .find(move |trait_item| { - trait_item.kind == ty::AssocKind::Fn - && tcx.hygienic_eq(impl_item.ident, trait_item.ident, trait_def_id) - }) - { - return tcx - .codegen_fn_attrs(trait_item.def_id) - .flags - .intersects(CodegenFnAttrFlags::TRACK_CALLER); - } + if let ty::AssocItemContainer::ImplContainer(_) = impl_item.container { + if let Some(trait_item) = impl_item.trait_item_def_id { + return tcx + .codegen_fn_attrs(trait_item) + .flags + .intersects(CodegenFnAttrFlags::TRACK_CALLER); } } } diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index ae8d262fcf1..5ff2a747541 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_typeck/src/collect/type_of.rs @@ -18,6 +18,7 @@ use super::{bad_placeholder, is_suggestable_infer_ty}; /// Computes the relevant generic parameter for a potential generic const argument. /// /// This should be called using the query `tcx.opt_const_param_of`. +#[instrument(level = "debug", skip(tcx))] pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<DefId> { // FIXME(generic_arg_infer): allow for returning DefIds of inference of // GenericArg::Infer below. This may require a change where GenericArg::Infer has some flag @@ -25,231 +26,228 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< use hir::*; let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - if let Node::AnonConst(_) = tcx.hir().get(hir_id) { - let parent_node_id = tcx.hir().get_parent_node(hir_id); - let parent_node = tcx.hir().get(parent_node_id); + match tcx.hir().get(hir_id) { + Node::AnonConst(_) => (), + _ => return None, + }; - match parent_node { - // This match arm is for when the def_id appears in a GAT whose - // path can't be resolved without typechecking e.g. - // - // trait Foo { - // type Assoc<const N: usize>; - // fn foo() -> Self::Assoc<3>; - // } - // - // In the above code we would call this query with the def_id of 3 and - // the parent_node we match on would be the hir node for Self::Assoc<3> - // - // `Self::Assoc<3>` cant be resolved without typchecking here as we - // didnt write <Self as Foo>::Assoc<3>. If we did then another match - // arm would handle this. - // - // I believe this match arm is only needed for GAT but I am not 100% sure - BoxyUwU - Node::Ty(hir_ty @ Ty { kind: TyKind::Path(QPath::TypeRelative(_, segment)), .. }) => { - // Find the Item containing the associated type so we can create an ItemCtxt. - // Using the ItemCtxt convert the HIR for the unresolved assoc type into a - // ty which is a fully resolved projection. - // For the code example above, this would mean converting Self::Assoc<3> - // into a ty::Projection(<Self as Foo>::Assoc<3>) - let item_hir_id = tcx - .hir() - .parent_iter(hir_id) - .filter(|(_, node)| matches!(node, Node::Item(_))) - .map(|(id, _)| id) - .next() - .unwrap(); - let item_did = tcx.hir().local_def_id(item_hir_id).to_def_id(); - let item_ctxt = &ItemCtxt::new(tcx, item_did) as &dyn crate::astconv::AstConv<'_>; - let ty = item_ctxt.ast_ty_to_ty(hir_ty); - - // Iterate through the generics of the projection to find the one that corresponds to - // the def_id that this query was called with. We filter to only const args here as a - // precaution for if it's ever allowed to elide lifetimes in GAT's. It currently isn't - // but it can't hurt to be safe ^^ - if let ty::Projection(projection) = ty.kind() { - let generics = tcx.generics_of(projection.item_def_id); - - let arg_index = segment - .args - .and_then(|args| { - args.args - .iter() - .filter(|arg| arg.is_const()) - .position(|arg| arg.id() == hir_id) - }) - .unwrap_or_else(|| { - bug!("no arg matching AnonConst in segment"); - }); + let parent_node_id = tcx.hir().get_parent_node(hir_id); + let parent_node = tcx.hir().get(parent_node_id); - return generics - .params - .iter() - .filter(|param| matches!(param.kind, ty::GenericParamDefKind::Const { .. })) - .nth(arg_index) - .map(|param| param.def_id); - } - - // I dont think it's possible to reach this but I'm not 100% sure - BoxyUwU - tcx.sess.delay_span_bug( - tcx.def_span(def_id), - "unexpected non-GAT usage of an anon const", - ); - return None; - } - Node::Expr(&Expr { - kind: - ExprKind::MethodCall(segment, ..) | ExprKind::Path(QPath::TypeRelative(_, segment)), - .. - }) => { - let body_owner = tcx.hir().local_def_id(tcx.hir().enclosing_body_owner(hir_id)); - let tables = tcx.typeck(body_owner); - // This may fail in case the method/path does not actually exist. - // As there is no relevant param for `def_id`, we simply return - // `None` here. - let type_dependent_def = tables.type_dependent_def_id(parent_node_id)?; - let idx = segment + let (generics, arg_idx) = match parent_node { + // This match arm is for when the def_id appears in a GAT whose + // path can't be resolved without typechecking e.g. + // + // trait Foo { + // type Assoc<const N: usize>; + // fn foo() -> Self::Assoc<3>; + // } + // + // In the above code we would call this query with the def_id of 3 and + // the parent_node we match on would be the hir node for Self::Assoc<3> + // + // `Self::Assoc<3>` cant be resolved without typchecking here as we + // didnt write <Self as Foo>::Assoc<3>. If we did then another match + // arm would handle this. + // + // I believe this match arm is only needed for GAT but I am not 100% sure - BoxyUwU + Node::Ty(hir_ty @ Ty { kind: TyKind::Path(QPath::TypeRelative(_, segment)), .. }) => { + // Find the Item containing the associated type so we can create an ItemCtxt. + // Using the ItemCtxt convert the HIR for the unresolved assoc type into a + // ty which is a fully resolved projection. + // For the code example above, this would mean converting Self::Assoc<3> + // into a ty::Projection(<Self as Foo>::Assoc<3>) + let item_hir_id = tcx + .hir() + .parent_iter(hir_id) + .filter(|(_, node)| matches!(node, Node::Item(_))) + .map(|(id, _)| id) + .next() + .unwrap(); + let item_did = tcx.hir().local_def_id(item_hir_id).to_def_id(); + let item_ctxt = &ItemCtxt::new(tcx, item_did) as &dyn crate::astconv::AstConv<'_>; + let ty = item_ctxt.ast_ty_to_ty(hir_ty); + + // Iterate through the generics of the projection to find the one that corresponds to + // the def_id that this query was called with. We filter to only const args here as a + // precaution for if it's ever allowed to elide lifetimes in GAT's. It currently isn't + // but it can't hurt to be safe ^^ + if let ty::Projection(projection) = ty.kind() { + let generics = tcx.generics_of(projection.item_def_id); + + let arg_index = segment .args .and_then(|args| { args.args .iter() - .filter(|arg| arg.is_const()) + .filter(|arg| arg.is_ty_or_const()) .position(|arg| arg.id() == hir_id) }) .unwrap_or_else(|| { bug!("no arg matching AnonConst in segment"); }); - tcx.generics_of(type_dependent_def) - .params - .iter() - .filter(|param| matches!(param.kind, ty::GenericParamDefKind::Const { .. })) - .nth(idx) - .map(|param| param.def_id) + (generics, arg_index) + } else { + // I dont think it's possible to reach this but I'm not 100% sure - BoxyUwU + tcx.sess.delay_span_bug( + tcx.def_span(def_id), + "unexpected non-GAT usage of an anon const", + ); + return None; } + } + Node::Expr(&Expr { + kind: + ExprKind::MethodCall(segment, ..) | ExprKind::Path(QPath::TypeRelative(_, segment)), + .. + }) => { + let body_owner = tcx.hir().local_def_id(tcx.hir().enclosing_body_owner(hir_id)); + let tables = tcx.typeck(body_owner); + // This may fail in case the method/path does not actually exist. + // As there is no relevant param for `def_id`, we simply return + // `None` here. + let type_dependent_def = tables.type_dependent_def_id(parent_node_id)?; + let idx = segment + .args + .and_then(|args| { + args.args + .iter() + .filter(|arg| arg.is_ty_or_const()) + .position(|arg| arg.id() == hir_id) + }) + .unwrap_or_else(|| { + bug!("no arg matching AnonConst in segment"); + }); - Node::Ty(&Ty { kind: TyKind::Path(_), .. }) - | Node::Expr(&Expr { kind: ExprKind::Path(_) | ExprKind::Struct(..), .. }) - | Node::TraitRef(..) - | Node::Pat(_) => { - let path = match parent_node { - Node::Ty(&Ty { kind: TyKind::Path(QPath::Resolved(_, path)), .. }) - | Node::TraitRef(&TraitRef { path, .. }) => &*path, - Node::Expr(&Expr { - kind: - ExprKind::Path(QPath::Resolved(_, path)) - | ExprKind::Struct(&QPath::Resolved(_, path), ..), - .. - }) => { - let body_owner = - tcx.hir().local_def_id(tcx.hir().enclosing_body_owner(hir_id)); - let _tables = tcx.typeck(body_owner); - &*path - } - Node::Pat(pat) => { - if let Some(path) = get_path_containing_arg_in_pat(pat, hir_id) { - path - } else { - tcx.sess.delay_span_bug( - tcx.def_span(def_id), - &format!( - "unable to find const parent for {} in pat {:?}", - hir_id, pat - ), - ); - return None; - } - } - _ => { - tcx.sess.delay_span_bug( - tcx.def_span(def_id), - &format!("unexpected const parent path {:?}", parent_node), - ); - return None; - } - }; - - // We've encountered an `AnonConst` in some path, so we need to - // figure out which generic parameter it corresponds to and return - // the relevant type. - let filtered = path - .segments - .iter() - .filter_map(|seg| seg.args.map(|args| (args.args, seg))) - .find_map(|(args, seg)| { - args.iter() - .filter(|arg| arg.is_const()) - .position(|arg| arg.id() == hir_id) - .map(|index| (index, seg)) - }); - let (arg_index, segment) = match filtered { - None => { - tcx.sess.delay_span_bug( - tcx.def_span(def_id), - "no arg matching AnonConst in path", - ); - return None; - } - Some(inner) => inner, - }; - - // Try to use the segment resolution if it is valid, otherwise we - // default to the path resolution. - let res = segment.res.filter(|&r| r != Res::Err).unwrap_or(path.res); - use def::CtorOf; - let generics = match res { - Res::Def(DefKind::Ctor(CtorOf::Variant, _), def_id) => tcx.generics_of( - tcx.parent(def_id).and_then(|def_id| tcx.parent(def_id)).unwrap(), - ), - Res::Def(DefKind::Variant | DefKind::Ctor(CtorOf::Struct, _), def_id) => { - tcx.generics_of(tcx.parent(def_id).unwrap()) - } - // Other `DefKind`s don't have generics and would ICE when calling - // `generics_of`. - Res::Def( - DefKind::Struct - | DefKind::Union - | DefKind::Enum - | DefKind::Trait - | DefKind::OpaqueTy - | DefKind::TyAlias - | DefKind::ForeignTy - | DefKind::TraitAlias - | DefKind::AssocTy - | DefKind::Fn - | DefKind::AssocFn - | DefKind::AssocConst - | DefKind::Impl, - def_id, - ) => tcx.generics_of(def_id), - Res::Err => { - tcx.sess.delay_span_bug(tcx.def_span(def_id), "anon const with Res::Err"); - return None; - } - _ => { - // If the user tries to specify generics on a type that does not take them, - // e.g. `usize<T>`, we may hit this branch, in which case we treat it as if - // no arguments have been passed. An error should already have been emitted. + (tcx.generics_of(type_dependent_def), idx) + } + + Node::Ty(&Ty { kind: TyKind::Path(_), .. }) + | Node::Expr(&Expr { kind: ExprKind::Path(_) | ExprKind::Struct(..), .. }) + | Node::TraitRef(..) + | Node::Pat(_) => { + let path = match parent_node { + Node::Ty(&Ty { kind: TyKind::Path(QPath::Resolved(_, path)), .. }) + | Node::TraitRef(&TraitRef { path, .. }) => &*path, + Node::Expr(&Expr { + kind: + ExprKind::Path(QPath::Resolved(_, path)) + | ExprKind::Struct(&QPath::Resolved(_, path), ..), + .. + }) => { + let body_owner = tcx.hir().local_def_id(tcx.hir().enclosing_body_owner(hir_id)); + let _tables = tcx.typeck(body_owner); + &*path + } + Node::Pat(pat) => { + if let Some(path) = get_path_containing_arg_in_pat(pat, hir_id) { + path + } else { tcx.sess.delay_span_bug( tcx.def_span(def_id), - &format!("unexpected anon const res {:?} in path: {:?}", res, path), + &format!("unable to find const parent for {} in pat {:?}", hir_id, pat), ); return None; } - }; - - generics - .params - .iter() - .filter(|param| matches!(param.kind, ty::GenericParamDefKind::Const { .. })) - .nth(arg_index) - .map(|param| param.def_id) + } + _ => { + tcx.sess.delay_span_bug( + tcx.def_span(def_id), + &format!("unexpected const parent path {:?}", parent_node), + ); + return None; + } + }; + + // We've encountered an `AnonConst` in some path, so we need to + // figure out which generic parameter it corresponds to and return + // the relevant type. + let filtered = path + .segments + .iter() + .filter_map(|seg| seg.args.map(|args| (args.args, seg))) + .find_map(|(args, seg)| { + args.iter() + .filter(|arg| arg.is_ty_or_const()) + .position(|arg| arg.id() == hir_id) + .map(|index| (index, seg)) + }); + let (arg_index, segment) = match filtered { + None => { + tcx.sess + .delay_span_bug(tcx.def_span(def_id), "no arg matching AnonConst in path"); + return None; + } + Some(inner) => inner, + }; + + // Try to use the segment resolution if it is valid, otherwise we + // default to the path resolution. + let res = segment.res.filter(|&r| r != Res::Err).unwrap_or(path.res); + use def::CtorOf; + let generics = match res { + Res::Def(DefKind::Ctor(CtorOf::Variant, _), def_id) => tcx + .generics_of(tcx.parent(def_id).and_then(|def_id| tcx.parent(def_id)).unwrap()), + Res::Def(DefKind::Variant | DefKind::Ctor(CtorOf::Struct, _), def_id) => { + tcx.generics_of(tcx.parent(def_id).unwrap()) + } + // Other `DefKind`s don't have generics and would ICE when calling + // `generics_of`. + Res::Def( + DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Trait + | DefKind::OpaqueTy + | DefKind::TyAlias + | DefKind::ForeignTy + | DefKind::TraitAlias + | DefKind::AssocTy + | DefKind::Fn + | DefKind::AssocFn + | DefKind::AssocConst + | DefKind::Impl, + def_id, + ) => tcx.generics_of(def_id), + Res::Err => { + tcx.sess.delay_span_bug(tcx.def_span(def_id), "anon const with Res::Err"); + return None; + } + _ => { + // If the user tries to specify generics on a type that does not take them, + // e.g. `usize<T>`, we may hit this branch, in which case we treat it as if + // no arguments have been passed. An error should already have been emitted. + tcx.sess.delay_span_bug( + tcx.def_span(def_id), + &format!("unexpected anon const res {:?} in path: {:?}", res, path), + ); + return None; + } + }; + + (generics, arg_index) + } + _ => return None, + }; + + debug!(?parent_node); + debug!(?generics, ?arg_idx); + generics + .params + .iter() + .filter(|param| param.kind.is_ty_or_const()) + .nth(match generics.has_self && generics.parent.is_none() { + true => arg_idx + 1, + false => arg_idx, + }) + .and_then(|param| match param.kind { + ty::GenericParamDefKind::Const { .. } => { + debug!(?param); + Some(param.def_id) } _ => None, - } - } else { - None - } + }) } fn get_path_containing_arg_in_pat<'hir>( diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs index 1ae0ff30364..352cdefc0b4 100644 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_typeck/src/expr_use_visitor.rs @@ -796,14 +796,14 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { ); match capture_info.capture_kind { - ty::UpvarCapture::ByValue(_) => { + ty::UpvarCapture::ByValue => { self.delegate_consume(&place_with_id, place_with_id.hir_id); } ty::UpvarCapture::ByRef(upvar_borrow) => { self.delegate.borrow( &place_with_id, place_with_id.hir_id, - upvar_borrow.kind, + upvar_borrow, ); } } diff --git a/compiler/rustc_typeck/src/variance/terms.rs b/compiler/rustc_typeck/src/variance/terms.rs index d7f9df668bf..36fbfc21ff5 100644 --- a/compiler/rustc_typeck/src/variance/terms.rs +++ b/compiler/rustc_typeck/src/variance/terms.rs @@ -86,7 +86,7 @@ pub fn determine_parameters_to_be_inferred<'a, 'tcx>( fn lang_items(tcx: TyCtxt<'_>) -> Vec<(hir::HirId, Vec<ty::Variance>)> { let lang_items = tcx.lang_items(); - let all = vec![ + let all = [ (lang_items.phantom_data(), vec![ty::Covariant]), (lang_items.unsafe_cell_type(), vec![ty::Invariant]), ]; diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index b3ff0fd0a31..265020209eb 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -6,7 +6,7 @@ repository = "https://github.com/rust-lang/rust.git" description = "The Rust core allocation and collections library" autotests = false autobenches = false -edition = "2018" +edition = "2021" [dependencies] core = { path = "../core" } diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 199c05dc5df..62153efbb39 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -16,7 +16,10 @@ use super::node::{self, marker, ForceResult::*, Handle, NodeRef, Root}; use super::search::SearchResult::*; mod entry; + +#[stable(feature = "rust1", since = "1.0.0")] pub use entry::{Entry, OccupiedEntry, OccupiedError, VacantEntry}; + use Entry::*; /// Minimum number of elements in a node that is not a root. diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs index c95aeeaa605..b39b5409ae4 100644 --- a/library/alloc/src/collections/btree/map/tests.rs +++ b/library/alloc/src/collections/btree/map/tests.rs @@ -728,7 +728,7 @@ fn test_range_large() { #[test] fn test_range_inclusive_max_value() { let max = usize::MAX; - let map: BTreeMap<_, _> = vec![(max, 0)].into_iter().collect(); + let map: BTreeMap<_, _> = [(max, 0)].into_iter().collect(); assert_eq!(map.range(max..=max).collect::<Vec<_>>(), &[(&max, &0)]); } @@ -2128,7 +2128,7 @@ fn test_into_iter_drop_leak_height_1() { #[test] fn test_into_keys() { - let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')]; + let vec = [(1, 'a'), (2, 'b'), (3, 'c')]; let map: BTreeMap<_, _> = vec.into_iter().collect(); let keys: Vec<_> = map.into_keys().collect(); diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 075becfb7d1..1259c53bfab 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -669,7 +669,7 @@ impl<T, A: Allocator> VecDeque<T, A> { /// ``` /// use std::collections::VecDeque; /// - /// let mut buf: VecDeque<i32> = vec![1].into_iter().collect(); + /// let mut buf: VecDeque<i32> = [1].into_iter().collect(); /// buf.reserve_exact(10); /// assert!(buf.capacity() >= 11); /// ``` @@ -692,7 +692,7 @@ impl<T, A: Allocator> VecDeque<T, A> { /// ``` /// use std::collections::VecDeque; /// - /// let mut buf: VecDeque<i32> = vec![1].into_iter().collect(); + /// let mut buf: VecDeque<i32> = [1].into_iter().collect(); /// buf.reserve(10); /// assert!(buf.capacity() >= 11); /// ``` @@ -1153,7 +1153,7 @@ impl<T, A: Allocator> VecDeque<T, A> { /// ``` /// use std::collections::VecDeque; /// - /// let v: VecDeque<_> = vec![1, 2, 3].into_iter().collect(); + /// let v: VecDeque<_> = [1, 2, 3].into_iter().collect(); /// let range = v.range(2..).copied().collect::<VecDeque<_>>(); /// assert_eq!(range, [3]); /// @@ -1188,7 +1188,7 @@ impl<T, A: Allocator> VecDeque<T, A> { /// ``` /// use std::collections::VecDeque; /// - /// let mut v: VecDeque<_> = vec![1, 2, 3].into_iter().collect(); + /// let mut v: VecDeque<_> = [1, 2, 3].into_iter().collect(); /// for v in v.range_mut(2..) { /// *v *= 2; /// } @@ -1235,7 +1235,7 @@ impl<T, A: Allocator> VecDeque<T, A> { /// ``` /// use std::collections::VecDeque; /// - /// let mut v: VecDeque<_> = vec![1, 2, 3].into_iter().collect(); + /// let mut v: VecDeque<_> = [1, 2, 3].into_iter().collect(); /// let drained = v.drain(2..).collect::<VecDeque<_>>(); /// assert_eq!(drained, [3]); /// assert_eq!(v, [1, 2]); @@ -2025,7 +2025,7 @@ impl<T, A: Allocator> VecDeque<T, A> { /// ``` /// use std::collections::VecDeque; /// - /// let mut buf: VecDeque<_> = vec![1, 2, 3].into_iter().collect(); + /// let mut buf: VecDeque<_> = [1, 2, 3].into_iter().collect(); /// let buf2 = buf.split_off(1); /// assert_eq!(buf, [1]); /// assert_eq!(buf2, [2, 3]); @@ -2091,8 +2091,8 @@ impl<T, A: Allocator> VecDeque<T, A> { /// ``` /// use std::collections::VecDeque; /// - /// let mut buf: VecDeque<_> = vec![1, 2].into_iter().collect(); - /// let mut buf2: VecDeque<_> = vec![3, 4].into_iter().collect(); + /// let mut buf: VecDeque<_> = [1, 2].into_iter().collect(); + /// let mut buf2: VecDeque<_> = [3, 4].into_iter().collect(); /// buf.append(&mut buf2); /// assert_eq!(buf, [1, 2, 3, 4]); /// assert_eq!(buf2, []); diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 7e663fab16a..1cbc2b65f4d 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -119,7 +119,6 @@ #![feature(inplace_iteration)] #![feature(iter_advance_by)] #![feature(layout_for_ptr)] -#![feature(maybe_uninit_extra)] #![feature(maybe_uninit_slice)] #![cfg_attr(test, feature(new_uninit))] #![feature(nonnull_slice_from_raw_parts)] diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 3ad48a1d283..78f989e730d 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -3004,14 +3004,12 @@ impl<T, A: Allocator, const N: usize> TryFrom<Vec<T, A>> for [T; N] { /// # Examples /// /// ``` - /// use std::convert::TryInto; /// assert_eq!(vec![1, 2, 3].try_into(), Ok([1, 2, 3])); /// assert_eq!(<Vec<i32>>::new().try_into(), Ok([])); /// ``` /// /// If the length doesn't match, the input comes back in `Err`: /// ``` - /// use std::convert::TryInto; /// let r: Result<[i32; 4], _> = (0..10).collect::<Vec<_>>().try_into(); /// assert_eq!(r, Err(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9])); /// ``` @@ -3019,7 +3017,6 @@ impl<T, A: Allocator, const N: usize> TryFrom<Vec<T, A>> for [T; N] { /// If you're fine with just getting a prefix of the `Vec<T>`, /// you can call [`.truncate(N)`](Vec::truncate) first. /// ``` - /// use std::convert::TryInto; /// let mut v = String::from("hello world").into_bytes(); /// v.sort(); /// v.truncate(2); diff --git a/library/alloc/tests/linked_list.rs b/library/alloc/tests/linked_list.rs index afcb9e03fd0..5f5bd9af2fe 100644 --- a/library/alloc/tests/linked_list.rs +++ b/library/alloc/tests/linked_list.rs @@ -304,7 +304,7 @@ fn test_show() { let list: LinkedList<_> = (0..10).collect(); assert_eq!(format!("{:?}", list), "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]"); - let list: LinkedList<_> = vec!["just", "one", "test", "more"].iter().cloned().collect(); + let list: LinkedList<_> = ["just", "one", "test", "more"].into_iter().collect(); assert_eq!(format!("{:?}", list), "[\"just\", \"one\", \"test\", \"more\"]"); } @@ -336,7 +336,7 @@ fn test_extend() { assert_eq!(a.len(), 4); assert!(a.iter().eq(&[1, 2, 3, 4])); - let b: LinkedList<_> = vec![5, 6, 7].into_iter().collect(); + let b: LinkedList<_> = [5, 6, 7].into_iter().collect(); a.extend(b); // specializes to `append` assert_eq!(a.len(), 7); @@ -375,7 +375,7 @@ fn drain_filter_empty() { #[test] fn drain_filter_zst() { - let mut list: LinkedList<_> = vec![(), (), (), (), ()].into_iter().collect(); + let mut list: LinkedList<_> = [(), (), (), (), ()].into_iter().collect(); let initial_len = list.len(); let mut count = 0; @@ -398,7 +398,7 @@ fn drain_filter_zst() { #[test] fn drain_filter_false() { - let mut list: LinkedList<_> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); + let mut list: LinkedList<_> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); let initial_len = list.len(); let mut count = 0; @@ -421,7 +421,7 @@ fn drain_filter_false() { #[test] fn drain_filter_true() { - let mut list: LinkedList<_> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); + let mut list: LinkedList<_> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); let initial_len = list.len(); let mut count = 0; @@ -447,7 +447,7 @@ fn drain_filter_true() { fn drain_filter_complex() { { // [+xxx++++++xxxxx++++x+x++] - let mut list = vec![ + let mut list = [ 1, 2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36, 37, 39, ] @@ -467,11 +467,10 @@ fn drain_filter_complex() { { // [xxx++++++xxxxx++++x+x++] - let mut list = vec![ - 2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36, 37, 39, - ] - .into_iter() - .collect::<LinkedList<_>>(); + let mut list = + [2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36, 37, 39] + .into_iter() + .collect::<LinkedList<_>>(); let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>(); assert_eq!(removed.len(), 10); @@ -487,7 +486,7 @@ fn drain_filter_complex() { { // [xxx++++++xxxxx++++x+x] let mut list = - vec![2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36] + [2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36] .into_iter() .collect::<LinkedList<_>>(); @@ -504,7 +503,7 @@ fn drain_filter_complex() { { // [xxxxxxxxxx+++++++++++] - let mut list = vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19] + let mut list = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19] .into_iter() .collect::<LinkedList<_>>(); @@ -518,7 +517,7 @@ fn drain_filter_complex() { { // [+++++++++++xxxxxxxxxx] - let mut list = vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20] + let mut list = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20] .into_iter() .collect::<LinkedList<_>>(); diff --git a/library/alloc/tests/string.rs b/library/alloc/tests/string.rs index 7be137131ff..893283e5a24 100644 --- a/library/alloc/tests/string.rs +++ b/library/alloc/tests/string.rs @@ -489,7 +489,7 @@ fn test_from_iterator() { b.extend(u.chars()); assert_eq!(s, b); - let c: String = vec![t, u].into_iter().collect(); + let c: String = [t, u].into_iter().collect(); assert_eq!(s, c); let mut d = t.to_string(); diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs index 77314282532..705914b4497 100644 --- a/library/alloc/tests/vec.rs +++ b/library/alloc/tests/vec.rs @@ -449,10 +449,10 @@ fn zero_sized_values() { #[test] fn test_partition() { - assert_eq!(vec![].into_iter().partition(|x: &i32| *x < 3), (vec![], vec![])); - assert_eq!(vec![1, 2, 3].into_iter().partition(|x| *x < 4), (vec![1, 2, 3], vec![])); - assert_eq!(vec![1, 2, 3].into_iter().partition(|x| *x < 2), (vec![1], vec![2, 3])); - assert_eq!(vec![1, 2, 3].into_iter().partition(|x| *x < 0), (vec![], vec![1, 2, 3])); + assert_eq!([].into_iter().partition(|x: &i32| *x < 3), (vec![], vec![])); + assert_eq!([1, 2, 3].into_iter().partition(|x| *x < 4), (vec![1, 2, 3], vec![])); + assert_eq!([1, 2, 3].into_iter().partition(|x| *x < 2), (vec![1], vec![2, 3])); + assert_eq!([1, 2, 3].into_iter().partition(|x| *x < 0), (vec![], vec![1, 2, 3])); } #[test] @@ -924,7 +924,7 @@ fn test_into_iter_debug() { #[test] fn test_into_iter_count() { - assert_eq!(vec![1, 2, 3].into_iter().count(), 3); + assert_eq!([1, 2, 3].into_iter().count(), 3); } #[test] @@ -933,7 +933,7 @@ fn test_into_iter_clone() { let v: Vec<i32> = it.collect(); assert_eq!(&v[..], slice); } - let mut it = vec![1, 2, 3].into_iter(); + let mut it = [1, 2, 3].into_iter(); iter_equal(it.clone(), &[1, 2, 3]); assert_eq!(it.next(), Some(1)); let mut it = it.rev(); @@ -972,7 +972,7 @@ fn test_into_iter_leak() { #[test] fn test_into_iter_advance_by() { - let mut i = vec![1, 2, 3, 4, 5].into_iter(); + let mut i = [1, 2, 3, 4, 5].into_iter(); i.advance_by(0).unwrap(); i.advance_back_by(0).unwrap(); assert_eq!(i.as_slice(), [1, 2, 3, 4, 5]); @@ -1799,7 +1799,7 @@ fn test_stable_pointers() { assert_eq!(*v0, 13); next_then_drop(v.splice(5..8, vec![1])); // replacement is smaller than original range assert_eq!(*v0, 13); - next_then_drop(v.splice(5..6, vec![1; 10].into_iter().filter(|_| true))); // lower bound not exact + next_then_drop(v.splice(5..6, [1; 10].into_iter().filter(|_| true))); // lower bound not exact assert_eq!(*v0, 13); // spare_capacity_mut diff --git a/library/alloc/tests/vec_deque.rs b/library/alloc/tests/vec_deque.rs index ddfb4c00c26..18954f094c6 100644 --- a/library/alloc/tests/vec_deque.rs +++ b/library/alloc/tests/vec_deque.rs @@ -927,8 +927,8 @@ fn test_as_mut_slices() { #[test] fn test_append() { - let mut a: VecDeque<_> = vec![1, 2, 3].into_iter().collect(); - let mut b: VecDeque<_> = vec![4, 5, 6].into_iter().collect(); + let mut a: VecDeque<_> = [1, 2, 3].into_iter().collect(); + let mut b: VecDeque<_> = [4, 5, 6].into_iter().collect(); // normal append a.append(&mut b); @@ -1209,7 +1209,7 @@ fn test_try_reserve() { { // Same basic idea, but with non-zero len - let mut ten_bytes: VecDeque<u8> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); + let mut ten_bytes: VecDeque<u8> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10).map_err(|e| e.kind()) { panic!("isize::MAX shouldn't trigger an overflow!"); @@ -1240,7 +1240,7 @@ fn test_try_reserve() { { // Same basic idea, but with interesting type size - let mut ten_u32s: VecDeque<u32> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); + let mut ten_u32s: VecDeque<u32> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_CAP / 4 - 10).map_err(|e| e.kind()) { @@ -1322,7 +1322,7 @@ fn test_try_reserve_exact() { } { - let mut ten_bytes: VecDeque<u8> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); + let mut ten_bytes: VecDeque<u8> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 10).map_err(|e| e.kind()) @@ -1355,7 +1355,7 @@ fn test_try_reserve_exact() { } { - let mut ten_u32s: VecDeque<u32> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); + let mut ten_u32s: VecDeque<u32> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_CAP / 4 - 10).map_err(|e| e.kind()) diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml index 6f10b9e4342..6bc4ba3cc0e 100644 --- a/library/core/Cargo.toml +++ b/library/core/Cargo.toml @@ -6,7 +6,7 @@ repository = "https://github.com/rust-lang/rust.git" description = "The Rust Core Library" autotests = false autobenches = false -edition = "2018" +edition = "2021" [lib] test = false diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 37292bf8e26..121aa634deb 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -66,8 +66,6 @@ where /// /// ```rust /// #![feature(array_from_fn)] -/// # // Apparently these doc tests are still on edition2018 -/// # use std::convert::TryInto; /// /// let array: Result<[u8; 5], _> = std::array::try_from_fn(|i| i.try_into()); /// assert_eq!(array, Ok([0, 1, 2, 3, 4])); diff --git a/library/core/src/char/convert.rs b/library/core/src/char/convert.rs index c4046d7496f..1774ddd7cbb 100644 --- a/library/core/src/char/convert.rs +++ b/library/core/src/char/convert.rs @@ -1,5 +1,6 @@ //! Character conversions. +use crate::char::TryFromCharError; use crate::convert::TryFrom; use crate::fmt; use crate::mem::transmute; @@ -166,6 +167,20 @@ impl const From<char> for u128 { } } +/// Map `char` with code point in U+0000..=U+00FF to byte in 0x00..=0xFF with same value, failing +/// if the code point is greater than U+00FF. +/// +/// See [`impl From<u8> for char`](char#impl-From<u8>) for details on the encoding. +#[stable(feature = "u8_from_char", since = "1.59.0")] +impl TryFrom<char> for u8 { + type Error = TryFromCharError; + + #[inline] + fn try_from(c: char) -> Result<u8, Self::Error> { + u8::try_from(u32::from(c)).map_err(|_| TryFromCharError(())) + } +} + /// Maps a byte in 0x00..=0xFF to a `char` whose code point has the same value, in U+0000..=U+00FF. /// /// Unicode is designed such that this effectively decodes bytes diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs index 5f30d5790a0..f65f84e93ae 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -544,3 +544,15 @@ impl fmt::Display for ToUppercase { fmt::Display::fmt(&self.0, f) } } + +/// The error type returned when a checked char conversion fails. +#[stable(feature = "u8_from_char", since = "1.59.0")] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct TryFromCharError(pub(crate) ()); + +#[stable(feature = "u8_from_char", since = "1.59.0")] +impl fmt::Display for TryFromCharError { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + "unicode code point out of range".fmt(fmt) + } +} diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index 1c2e673d604..5566c2ffe87 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -91,7 +91,7 @@ pub use num::FloatToInt; /// ```rust /// use std::convert::identity; /// -/// let iter = vec![Some(1), None, Some(3)].into_iter(); +/// let iter = [Some(1), None, Some(3)].into_iter(); /// let filtered = iter.filter_map(identity).collect::<Vec<_>>(); /// assert_eq!(vec![1, 3], filtered); /// ``` @@ -426,8 +426,6 @@ pub trait TryInto<T>: Sized { /// `TryFrom<T>` can be implemented as follows: /// /// ``` -/// use std::convert::TryFrom; -/// /// struct GreaterThanZero(i32); /// /// impl TryFrom<i32> for GreaterThanZero { @@ -448,8 +446,6 @@ pub trait TryInto<T>: Sized { /// As described, [`i32`] implements `TryFrom<`[`i64`]`>`: /// /// ``` -/// use std::convert::TryFrom; -/// /// let big_number = 1_000_000_000_000i64; /// // Silently truncates `big_number`, requires detecting /// // and handling the truncation after the fact. @@ -485,9 +481,10 @@ pub trait TryFrom<T>: Sized { // As lifts over & #[stable(feature = "rust1", since = "1.0.0")] -impl<T: ?Sized, U: ?Sized> AsRef<U> for &T +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl<T: ?Sized, U: ?Sized> const AsRef<U> for &T where - T: AsRef<U>, + T: ~const AsRef<U>, { fn as_ref(&self) -> &U { <T as AsRef<U>>::as_ref(*self) @@ -496,9 +493,10 @@ where // As lifts over &mut #[stable(feature = "rust1", since = "1.0.0")] -impl<T: ?Sized, U: ?Sized> AsRef<U> for &mut T +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl<T: ?Sized, U: ?Sized> const AsRef<U> for &mut T where - T: AsRef<U>, + T: ~const AsRef<U>, { fn as_ref(&self) -> &U { <T as AsRef<U>>::as_ref(*self) @@ -515,9 +513,10 @@ where // AsMut lifts over &mut #[stable(feature = "rust1", since = "1.0.0")] -impl<T: ?Sized, U: ?Sized> AsMut<U> for &mut T +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl<T: ?Sized, U: ?Sized> const AsMut<U> for &mut T where - T: AsMut<U>, + T: ~const AsMut<U>, { fn as_mut(&mut self) -> &mut U { (*self).as_mut() @@ -571,9 +570,10 @@ impl<T> const From<!> for T { // TryFrom implies TryInto #[stable(feature = "try_from", since = "1.34.0")] -impl<T, U> TryInto<U> for T +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl<T, U> const TryInto<U> for T where - U: TryFrom<T>, + U: ~const TryFrom<T>, { type Error = U::Error; @@ -585,9 +585,10 @@ where // Infallible conversions are semantically equivalent to fallible conversions // with an uninhabited error type. #[stable(feature = "try_from", since = "1.34.0")] -impl<T, U> TryFrom<U> for T +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl<T, U> const TryFrom<U> for T where - U: Into<T>, + U: ~const Into<T>, { type Error = Infallible; diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 4ecc3b0c7f8..acbb612352b 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -961,7 +961,7 @@ extern "rust-intrinsic" { /// Below are common applications of `transmute` which can be replaced with safer /// constructs. /// - /// Turning raw bytes(`&[u8]`) to `u32`, `f64`, etc.: + /// Turning raw bytes (`&[u8]`) into `u32`, `f64`, etc.: /// /// ``` /// let raw_bytes = [0x78, 0x56, 0x34, 0x12]; diff --git a/library/core/src/iter/adapters/map.rs b/library/core/src/iter/adapters/map.rs index 449650a22f4..b2ed82508dd 100644 --- a/library/core/src/iter/adapters/map.rs +++ b/library/core/src/iter/adapters/map.rs @@ -19,7 +19,7 @@ use crate::ops::Try; /// you can also [`map`] backwards: /// /// ```rust -/// let v: Vec<i32> = vec![1, 2, 3].into_iter().map(|x| x + 1).rev().collect(); +/// let v: Vec<i32> = [1, 2, 3].into_iter().map(|x| x + 1).rev().collect(); /// /// assert_eq!(v, [4, 3, 2]); /// ``` @@ -32,7 +32,7 @@ use crate::ops::Try; /// ```rust /// let mut c = 0; /// -/// for pair in vec!['a', 'b', 'c'].into_iter() +/// for pair in ['a', 'b', 'c'].into_iter() /// .map(|letter| { c += 1; (letter, c) }) { /// println!("{:?}", pair); /// } @@ -49,7 +49,7 @@ use crate::ops::Try; /// ```rust /// let mut c = 0; /// -/// for pair in vec!['a', 'b', 'c'].into_iter() +/// for pair in ['a', 'b', 'c'].into_iter() /// .map(|letter| { c += 1; (letter, c) }) /// .rev() { /// println!("{:?}", pair); diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs index b1b917775c3..db8776ac741 100644 --- a/library/core/src/iter/adapters/mod.rs +++ b/library/core/src/iter/adapters/mod.rs @@ -24,6 +24,7 @@ mod take; mod take_while; mod zip; +#[stable(feature = "rust1", since = "1.0.0")] pub use self::{ chain::Chain, cycle::Cycle, enumerate::Enumerate, filter::Filter, filter_map::FilterMap, flatten::FlatMap, fuse::Fuse, inspect::Inspect, map::Map, peekable::Peekable, rev::Rev, diff --git a/library/core/src/iter/sources.rs b/library/core/src/iter/sources.rs index de0663141e2..37b6f2e2565 100644 --- a/library/core/src/iter/sources.rs +++ b/library/core/src/iter/sources.rs @@ -6,6 +6,7 @@ mod repeat; mod repeat_with; mod successors; +#[stable(feature = "rust1", since = "1.0.0")] pub use self::repeat::{repeat, Repeat}; #[stable(feature = "iter_empty", since = "1.2.0")] diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index 56fad602cf9..fc14620a2df 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -15,8 +15,6 @@ /// Basic usage: /// /// ``` -/// use std::iter::FromIterator; -/// /// let five_fives = std::iter::repeat(5).take(5); /// /// let v = Vec::from_iter(five_fives); @@ -37,8 +35,6 @@ /// Implementing `FromIterator` for your type: /// /// ``` -/// use std::iter::FromIterator; -/// /// // A sample collection, that's just a wrapper over Vec<T> /// #[derive(Debug)] /// struct MyCollection(Vec<i32>); @@ -102,8 +98,6 @@ pub trait FromIterator<A>: Sized { /// Basic usage: /// /// ``` - /// use std::iter::FromIterator; - /// /// let five_fives = std::iter::repeat(5).take(5); /// /// let v = Vec::from_iter(five_fives); @@ -130,7 +124,7 @@ pub trait FromIterator<A>: Sized { /// Basic usage: /// /// ``` -/// let v = vec![1, 2, 3]; +/// let v = [1, 2, 3]; /// let mut iter = v.into_iter(); /// /// assert_eq!(Some(1), iter.next()); @@ -221,7 +215,7 @@ pub trait IntoIterator { /// Basic usage: /// /// ``` - /// let v = vec![1, 2, 3]; + /// let v = [1, 2, 3]; /// let mut iter = v.into_iter(); /// /// assert_eq!(Some(1), iter.next()); diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 9a9a844f41b..1d947297463 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -590,7 +590,7 @@ pub trait Iterator { /// #[derive(PartialEq, Debug)] /// struct NotClone(usize); /// - /// let v = vec![NotClone(0), NotClone(1), NotClone(2)]; + /// let v = [NotClone(0), NotClone(1), NotClone(2)]; /// let mut it = v.into_iter().intersperse_with(|| NotClone(99)); /// /// assert_eq!(it.next(), Some(NotClone(0))); // The first element from `v`. @@ -1155,8 +1155,6 @@ pub trait Iterator { /// Stopping after an initial [`None`]: /// /// ``` - /// use std::convert::TryFrom; - /// /// let a = [0, 1, 2, -3, 4, 5, -6]; /// /// let iter = a.iter().map_while(|x| u32::try_from(*x).ok()); @@ -1172,8 +1170,6 @@ pub trait Iterator { /// removed: /// /// ``` - /// use std::convert::TryFrom; - /// /// let a = [1, 2, -3, 4]; /// let mut iter = a.iter(); /// @@ -1274,7 +1270,7 @@ pub trait Iterator { /// `take` will limit itself to the size of the underlying iterator: /// /// ``` - /// let v = vec![1, 2]; + /// let v = [1, 2]; /// let mut iter = v.into_iter().take(5); /// assert_eq!(iter.next(), Some(1)); /// assert_eq!(iter.next(), Some(2)); @@ -1608,7 +1604,7 @@ pub trait Iterator { /// Basic usage: /// /// ``` - /// let mut words = vec!["hello", "world", "of", "Rust"].into_iter(); + /// let mut words = ["hello", "world", "of", "Rust"].into_iter(); /// /// // Take the first two words. /// let hello_world: Vec<_> = words.by_ref().take(2).collect(); @@ -2704,7 +2700,7 @@ pub trait Iterator { /// incomparable. You can work around this by using [`Iterator::reduce`]: /// ``` /// assert_eq!( - /// vec![2.4, f32::NAN, 1.3] + /// [2.4, f32::NAN, 1.3] /// .into_iter() /// .reduce(f32::max) /// .unwrap(), @@ -2742,7 +2738,7 @@ pub trait Iterator { /// incomparable. You can work around this by using [`Iterator::reduce`]: /// ``` /// assert_eq!( - /// vec![2.4, f32::NAN, 1.3] + /// [2.4, f32::NAN, 1.3] /// .into_iter() /// .reduce(f32::min) /// .unwrap(), diff --git a/library/core/src/iter/traits/mod.rs b/library/core/src/iter/traits/mod.rs index ffd745a46b1..ed0fb634dbf 100644 --- a/library/core/src/iter/traits/mod.rs +++ b/library/core/src/iter/traits/mod.rs @@ -5,15 +5,17 @@ mod exact_size; mod iterator; mod marker; -pub use self::accum::{Product, Sum}; -pub use self::collect::{Extend, FromIterator, IntoIterator}; -pub use self::double_ended::DoubleEndedIterator; -pub use self::exact_size::ExactSizeIterator; #[stable(feature = "rust1", since = "1.0.0")] -pub use self::iterator::Iterator; +pub use self::{ + accum::{Product, Sum}, + collect::{Extend, FromIterator, IntoIterator}, + double_ended::DoubleEndedIterator, + exact_size::ExactSizeIterator, + iterator::Iterator, + marker::{FusedIterator, TrustedLen}, +}; + #[unstable(issue = "none", feature = "inplace_iteration")] pub use self::marker::InPlaceIterable; #[unstable(feature = "trusted_step", issue = "85731")] pub use self::marker::TrustedStep; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::marker::{FusedIterator, TrustedLen}; diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 3b0e4a31db1..dd2f7306356 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -330,7 +330,7 @@ impl<T> MaybeUninit<T> { /// # Examples /// /// ```no_run - /// #![feature(maybe_uninit_uninit_array, maybe_uninit_extra, maybe_uninit_slice)] + /// #![feature(maybe_uninit_uninit_array, maybe_uninit_slice)] /// /// use std::mem::MaybeUninit; /// @@ -662,7 +662,6 @@ impl<T> MaybeUninit<T> { /// Correct usage of this method: /// /// ```rust - /// #![feature(maybe_uninit_extra)] /// use std::mem::MaybeUninit; /// /// let mut x = MaybeUninit::<u32>::uninit(); @@ -683,7 +682,6 @@ impl<T> MaybeUninit<T> { /// *Incorrect* usage of this method: /// /// ```rust,no_run - /// #![feature(maybe_uninit_extra)] /// use std::mem::MaybeUninit; /// /// let mut x = MaybeUninit::<Option<Vec<u32>>>::uninit(); @@ -693,8 +691,8 @@ impl<T> MaybeUninit<T> { /// // We now created two copies of the same vector, leading to a double-free ⚠️ when /// // they both get dropped! /// ``` - #[unstable(feature = "maybe_uninit_extra", issue = "63567")] - #[rustc_const_unstable(feature = "maybe_uninit_extra", issue = "63567")] + #[stable(feature = "maybe_uninit_extra", since = "1.60.0")] + #[rustc_const_unstable(feature = "const_maybe_uninit_assume_init_read", issue = "63567")] #[inline(always)] #[track_caller] pub const unsafe fn assume_init_read(&self) -> T { @@ -728,7 +726,7 @@ impl<T> MaybeUninit<T> { /// /// [`assume_init`]: MaybeUninit::assume_init /// [`Vec<T>`]: ../../std/vec/struct.Vec.html - #[unstable(feature = "maybe_uninit_extra", issue = "63567")] + #[stable(feature = "maybe_uninit_extra", since = "1.60.0")] pub unsafe fn assume_init_drop(&mut self) { // SAFETY: the caller must guarantee that `self` is initialized and // satisfies all invariants of `T`. diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 7d005666a74..989ec0639cd 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -1045,6 +1045,10 @@ pub const fn discriminant<T>(v: &T) -> Discriminant<T> { /// return value is unspecified. Equally, if `T` is an enum with more variants than `usize::MAX` /// the return value is unspecified. Uninhabited variants will be counted. /// +/// Note that an enum may be expanded with additional variants in the future +/// as a non-breaking change, for example if it is marked `#[non_exhaustive]`, +/// which will change the result of this function. +/// /// # Examples /// /// ``` diff --git a/library/core/src/num/bignum.rs b/library/core/src/num/bignum.rs index 8a06a098882..98d8a8a1d74 100644 --- a/library/core/src/num/bignum.rs +++ b/library/core/src/num/bignum.rs @@ -158,24 +158,15 @@ macro_rules! define_bignum { /// Returns the number of bits necessary to represent this value. Note that zero /// is considered to need 0 bits. pub fn bit_length(&self) -> usize { - // Skip over the most significant digits which are zero. + let digitbits = <$ty>::BITS as usize; let digits = self.digits(); - let zeros = digits.iter().rev().take_while(|&&x| x == 0).count(); - let end = digits.len() - zeros; - let nonzero = &digits[..end]; - - if nonzero.is_empty() { + // Find the most significant non-zero digit. + let msd = digits.iter().rposition(|&x| x != 0); + match msd { + Some(msd) => msd * digitbits + digits[msd].log2() as usize + 1, // There are no non-zero digits, i.e., the number is zero. - return 0; - } - // This could be optimized with leading_zeros() and bit shifts, but that's - // probably not worth the hassle. - let digitbits = <$ty>::BITS as usize; - let mut i = nonzero.len() * digitbits - 1; - while self.get_bit(i) == 0 { - i -= 1; + _ => 0, } - i + 1 } /// Adds `other` to itself and returns its own mutable reference. diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index c4a232ef36c..85ceede5b9e 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -628,6 +628,7 @@ impl f32 { /// /// assert!(abs_difference <= f32::EPSILON); /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn recip(self) -> f32 { @@ -684,6 +685,7 @@ impl f32 { /// ``` /// /// If one of the arguments is NaN, then the other argument is returned. + #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn max(self, other: f32) -> f32 { @@ -703,6 +705,7 @@ impl f32 { /// ``` /// /// If one of the arguments is NaN, then the other argument is returned. + #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn min(self, other: f32) -> f32 { @@ -726,6 +729,7 @@ impl f32 { /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. /// Note that this follows the semantics specified in IEEE 754-2019. + #[must_use = "this returns the result of the comparison, without modifying either input"] #[unstable(feature = "float_minimum_maximum", issue = "91079")] #[inline] pub fn maximum(self, other: f32) -> f32 { @@ -757,6 +761,7 @@ impl f32 { /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. /// Note that this follows the semantics specified in IEEE 754-2019. + #[must_use = "this returns the result of the comparison, without modifying either input"] #[unstable(feature = "float_minimum_maximum", issue = "91079")] #[inline] pub fn minimum(self, other: f32) -> f32 { diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 85ee6aa2cb8..4049c95b130 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -643,6 +643,7 @@ impl f64 { /// /// assert!(abs_difference < 1e-10); /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn recip(self) -> f64 { @@ -700,6 +701,7 @@ impl f64 { /// ``` /// /// If one of the arguments is NaN, then the other argument is returned. + #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn max(self, other: f64) -> f64 { @@ -719,6 +721,7 @@ impl f64 { /// ``` /// /// If one of the arguments is NaN, then the other argument is returned. + #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn min(self, other: f64) -> f64 { @@ -742,6 +745,7 @@ impl f64 { /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. /// Note that this follows the semantics specified in IEEE 754-2019. + #[must_use = "this returns the result of the comparison, without modifying either input"] #[unstable(feature = "float_minimum_maximum", issue = "91079")] #[inline] pub fn maximum(self, other: f64) -> f64 { @@ -773,6 +777,7 @@ impl f64 { /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. /// Note that this follows the semantics specified in IEEE 754-2019. + #[must_use = "this returns the result of the comparison, without modifying either input"] #[unstable(feature = "float_minimum_maximum", issue = "91079")] #[inline] pub fn minimum(self, other: f64) -> f64 { diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index e6ae4afd7c1..6f7c5a6d119 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -2602,8 +2602,6 @@ macro_rules! int_impl { /// When starting from a slice rather than an array, fallible conversion APIs can be used: /// /// ``` - /// use std::convert::TryInto; - /// #[doc = concat!("fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {")] #[doc = concat!(" let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());")] /// *input = rest; @@ -2633,8 +2631,6 @@ macro_rules! int_impl { /// When starting from a slice rather than an array, fallible conversion APIs can be used: /// /// ``` - /// use std::convert::TryInto; - /// #[doc = concat!("fn read_le_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {")] #[doc = concat!(" let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());")] /// *input = rest; @@ -2675,8 +2671,6 @@ macro_rules! int_impl { /// When starting from a slice rather than an array, fallible conversion APIs can be used: /// /// ``` - /// use std::convert::TryInto; - /// #[doc = concat!("fn read_ne_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {")] #[doc = concat!(" let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());")] /// *input = rest; diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 3cc454baf35..1dd8b0a18ab 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -2323,8 +2323,6 @@ macro_rules! uint_impl { /// When starting from a slice rather than an array, fallible conversion APIs can be used: /// /// ``` - /// use std::convert::TryInto; - /// #[doc = concat!("fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {")] #[doc = concat!(" let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());")] /// *input = rest; @@ -2354,8 +2352,6 @@ macro_rules! uint_impl { /// When starting from a slice rather than an array, fallible conversion APIs can be used: /// /// ``` - /// use std::convert::TryInto; - /// #[doc = concat!("fn read_le_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {")] #[doc = concat!(" let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());")] /// *input = rest; @@ -2396,8 +2392,6 @@ macro_rules! uint_impl { /// When starting from a slice rather than an array, fallible conversion APIs can be used: /// /// ``` - /// use std::convert::TryInto; - /// #[doc = concat!("fn read_ne_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {")] #[doc = concat!(" let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());")] /// *input = rest; diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 1ec119a71e4..8adfb6f4bcf 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -270,7 +270,7 @@ //! let mut bt = BTreeMap::new(); //! bt.insert(20u8, "foo"); //! bt.insert(42u8, "bar"); -//! let res = vec![0u8, 1, 11, 200, 22] +//! let res = [0u8, 1, 11, 200, 22] //! .into_iter() //! .map(|x| { //! // `checked_sub()` returns `None` on error @@ -390,10 +390,10 @@ //! [impl-FromIterator]: Option#impl-FromIterator%3COption%3CA%3E%3E //! //! ``` -//! let v = vec![Some(2), Some(4), None, Some(8)]; +//! let v = [Some(2), Some(4), None, Some(8)]; //! let res: Option<Vec<_>> = v.into_iter().collect(); //! assert_eq!(res, None); -//! let v = vec![Some(2), Some(4), Some(8)]; +//! let v = [Some(2), Some(4), Some(8)]; //! let res: Option<Vec<_>> = v.into_iter().collect(); //! assert_eq!(res, Some(vec![2, 4, 8])); //! ``` @@ -407,10 +407,10 @@ //! [impl-Sum]: Option#impl-Sum%3COption%3CU%3E%3E //! //! ``` -//! let v = vec![None, Some(1), Some(2), Some(3)]; +//! let v = [None, Some(1), Some(2), Some(3)]; //! let res: Option<i32> = v.into_iter().sum(); //! assert_eq!(res, None); -//! let v = vec![Some(1), Some(2), Some(21)]; +//! let v = [Some(1), Some(2), Some(21)]; //! let res: Option<i32> = v.into_iter().product(); //! assert_eq!(res, Some(42)); //! ``` diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index eedea6562bd..ccb82cda54e 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -51,6 +51,7 @@ pub const fn panic(expr: &'static str) -> ! { #[inline] #[track_caller] #[lang = "panic_str"] // needed for `non-fmt-panics` lint +#[rustc_const_unstable(feature = "core_panic", issue = "none")] pub const fn panic_str(expr: &str) -> ! { panic_display(&expr); } @@ -59,6 +60,7 @@ pub const fn panic_str(expr: &str) -> ! { #[track_caller] #[lang = "panic_display"] // needed for const-evaluated panics #[rustc_do_not_const_check] // hooked by const-eval +#[rustc_const_unstable(feature = "core_panic", issue = "none")] pub const fn panic_display<T: fmt::Display>(x: &T) -> ! { panic_fmt(format_args!("{}", *x)); } @@ -89,6 +91,7 @@ fn panic_bounds_check(index: usize, len: usize) -> ! { #[track_caller] #[lang = "panic_fmt"] // needed for const-evaluated panics #[rustc_do_not_const_check] // hooked by const-eval +#[rustc_const_unstable(feature = "core_panic", issue = "none")] pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { if cfg!(feature = "panic_immediate_abort") { super::intrinsics::abort() @@ -109,6 +112,7 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { /// This function is used instead of panic_fmt in const eval. #[lang = "const_panic_fmt"] +#[rustc_const_unstable(feature = "core_panic", issue = "none")] pub const fn const_panic_fmt(fmt: fmt::Arguments<'_>) -> ! { if let Some(msg) = fmt.as_str() { panic_str(msg); diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index a93327a0132..7b826f921ca 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -48,6 +48,16 @@ impl<T: ?Sized> *const T { self as _ } + /// Changes constness without changing the type. + /// + /// This is a bit safer than `as` because it wouldn't silently change the type if the code is + /// refactored. + #[unstable(feature = "ptr_const_cast", issue = "92675")] + #[rustc_const_unstable(feature = "ptr_const_cast", issue = "92675")] + pub const fn as_mut(self) -> *mut T { + self as _ + } + /// Casts a pointer to its raw bits. /// /// This is equivalent to `as usize`, but is more specific to enhance readability. diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 5fd3b2ebc60..6c50d405297 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -47,6 +47,20 @@ impl<T: ?Sized> *mut T { self as _ } + /// Changes constness without changing the type. + /// + /// This is a bit safer than `as` because it wouldn't silently change the type if the code is + /// refactored. + /// + /// While not strictly required (`*mut T` coerces to `*const T`), this is provided for symmetry + /// with `as_mut()` on `*const T` and may have documentation value if used instead of implicit + /// coercion. + #[unstable(feature = "ptr_const_cast", issue = "92675")] + #[rustc_const_unstable(feature = "ptr_const_cast", issue = "92675")] + pub const fn as_const(self) -> *const T { + self as _ + } + /// Casts a pointer to its raw bits. /// /// This is equivalent to `as usize`, but is more specific to enhance readability. diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 575fd2b42d2..b8f0d84746c 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -436,7 +436,7 @@ //! # use std::str::FromStr; //! let mut results = vec![]; //! let mut errs = vec![]; -//! let nums: Vec<_> = vec!["17", "not a number", "99", "-27", "768"] +//! let nums: Vec<_> = ["17", "not a number", "99", "-27", "768"] //! .into_iter() //! .map(u8::from_str) //! // Save clones of the raw `Result` values to inspect @@ -462,10 +462,10 @@ //! [impl-FromIterator]: Result#impl-FromIterator%3CResult%3CA%2C%20E%3E%3E //! //! ``` -//! let v = vec![Ok(2), Ok(4), Err("err!"), Ok(8)]; +//! let v = [Ok(2), Ok(4), Err("err!"), Ok(8)]; //! let res: Result<Vec<_>, &str> = v.into_iter().collect(); //! assert_eq!(res, Err("err!")); -//! let v = vec![Ok(2), Ok(4), Ok(8)]; +//! let v = [Ok(2), Ok(4), Ok(8)]; //! let res: Result<Vec<_>, &str> = v.into_iter().collect(); //! assert_eq!(res, Ok(vec![2, 4, 8])); //! ``` @@ -479,10 +479,10 @@ //! [impl-Sum]: Result#impl-Sum%3CResult%3CU%2C%20E%3E%3E //! //! ``` -//! let v = vec![Err("error!"), Ok(1), Ok(2), Ok(3), Err("foo")]; +//! let v = [Err("error!"), Ok(1), Ok(2), Ok(3), Err("foo")]; //! let res: Result<i32, &str> = v.into_iter().sum(); //! assert_eq!(res, Err("error!")); -//! let v: Vec<Result<i32, &str>> = vec![Ok(1), Ok(2), Ok(21)]; +//! let v = [Ok(1), Ok(2), Ok(21)]; //! let res: Result<i32, &str> = v.into_iter().product(); //! assert_eq!(res, Ok(42)); //! ``` diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 1dd3b2d8e3c..27243d8ca70 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -333,10 +333,10 @@ impl AtomicBool { #[inline] #[cfg(target_has_atomic_equal_alignment = "8")] #[unstable(feature = "atomic_from_mut", issue = "76314")] - pub fn from_mut(v: &mut bool) -> &Self { + pub fn from_mut(v: &mut bool) -> &mut Self { // SAFETY: the mutable reference guarantees unique ownership, and // alignment of both `bool` and `Self` is 1. - unsafe { &*(v as *mut bool as *mut Self) } + unsafe { &mut *(v as *mut bool as *mut Self) } } /// Consumes the atomic and returns the contained value. @@ -934,14 +934,14 @@ impl<T> AtomicPtr<T> { #[inline] #[cfg(target_has_atomic_equal_alignment = "ptr")] #[unstable(feature = "atomic_from_mut", issue = "76314")] - pub fn from_mut(v: &mut *mut T) -> &Self { + pub fn from_mut(v: &mut *mut T) -> &mut Self { use crate::mem::align_of; let [] = [(); align_of::<AtomicPtr<()>>() - align_of::<*mut ()>()]; // SAFETY: // - the mutable reference guarantees unique ownership. // - the alignment of `*mut T` and `Self` is the same on all platforms // supported by rust, as verified above. - unsafe { &*(v as *mut *mut T as *mut Self) } + unsafe { &mut *(v as *mut *mut T as *mut Self) } } /// Consumes the atomic and returns the contained value. @@ -1447,14 +1447,14 @@ macro_rules! atomic_int { #[inline] #[$cfg_align] #[unstable(feature = "atomic_from_mut", issue = "76314")] - pub fn from_mut(v: &mut $int_type) -> &Self { + pub fn from_mut(v: &mut $int_type) -> &mut Self { use crate::mem::align_of; let [] = [(); align_of::<Self>() - align_of::<$int_type>()]; // SAFETY: // - the mutable reference guarantees unique ownership. // - the alignment of `$int_type` and `Self` is the // same, as promised by $cfg_align and verified above. - unsafe { &*(v as *mut $int_type as *mut Self) } + unsafe { &mut *(v as *mut $int_type as *mut Self) } } /// Consumes the atomic and returns the contained value. diff --git a/library/core/tests/iter/adapters/intersperse.rs b/library/core/tests/iter/adapters/intersperse.rs index b336c03b5ad..72ae59b6b2f 100644 --- a/library/core/tests/iter/adapters/intersperse.rs +++ b/library/core/tests/iter/adapters/intersperse.rs @@ -74,7 +74,7 @@ fn test_intersperse_with() { struct NotClone { u: u32, } - let r = vec![NotClone { u: 0 }, NotClone { u: 1 }] + let r = [NotClone { u: 0 }, NotClone { u: 1 }] .into_iter() .intersperse_with(|| NotClone { u: 2 }) .collect::<Vec<_>>(); @@ -120,7 +120,7 @@ fn test_intersperse_fold() { #[test] fn test_intersperse_collect_string() { - let contents = vec![1, 2, 3]; + let contents = [1, 2, 3]; let contents_string = contents .into_iter() diff --git a/library/core/tests/iter/adapters/peekable.rs b/library/core/tests/iter/adapters/peekable.rs index 390414d4aa2..c1a1c29b609 100644 --- a/library/core/tests/iter/adapters/peekable.rs +++ b/library/core/tests/iter/adapters/peekable.rs @@ -144,7 +144,7 @@ fn test_iterator_peekable_rfold() { #[test] fn test_iterator_peekable_next_if_eq() { // first, try on references - let xs = vec!["Heart", "of", "Gold"]; + let xs = ["Heart", "of", "Gold"]; let mut it = xs.into_iter().peekable(); // try before `peek()` assert_eq!(it.next_if_eq(&"trillian"), None); @@ -157,7 +157,7 @@ fn test_iterator_peekable_next_if_eq() { assert_eq!(it.next(), Some("Gold")); // make sure comparison works for owned values - let xs = vec![String::from("Ludicrous"), "speed".into()]; + let xs = [String::from("Ludicrous"), "speed".into()]; let mut it = xs.into_iter().peekable(); // make sure basic functionality works assert_eq!(it.next_if_eq("Ludicrous"), Some("Ludicrous".into())); @@ -167,7 +167,7 @@ fn test_iterator_peekable_next_if_eq() { #[test] fn test_iterator_peekable_mut() { - let mut it = vec![1, 2, 3].into_iter().peekable(); + let mut it = [1, 2, 3].into_iter().peekable(); if let Some(p) = it.peek_mut() { if *p == 1 { *p = 5; diff --git a/library/core/tests/iter/traits/iterator.rs b/library/core/tests/iter/traits/iterator.rs index d38bca1e3b3..bb4da831412 100644 --- a/library/core/tests/iter/traits/iterator.rs +++ b/library/core/tests/iter/traits/iterator.rs @@ -456,25 +456,25 @@ fn test_find_map() { #[test] fn test_try_reduce() { - let v: Vec<usize> = vec![1, 2, 3, 4, 5]; + let v = [1usize, 2, 3, 4, 5]; let sum = v.into_iter().try_reduce(|x, y| x.checked_add(y)); assert_eq!(sum, Some(Some(15))); - let v: Vec<usize> = vec![1, 2, 3, 4, 5, usize::MAX]; + let v = [1, 2, 3, 4, 5, usize::MAX]; let sum = v.into_iter().try_reduce(|x, y| x.checked_add(y)); assert_eq!(sum, None); - let v: Vec<usize> = Vec::new(); + let v: [usize; 0] = []; let sum = v.into_iter().try_reduce(|x, y| x.checked_add(y)); assert_eq!(sum, Some(None)); - let v = vec!["1", "2", "3", "4", "5"]; + let v = ["1", "2", "3", "4", "5"]; let max = v.into_iter().try_reduce(|x, y| { if x.parse::<usize>().ok()? > y.parse::<usize>().ok()? { Some(x) } else { Some(y) } }); assert_eq!(max, Some(Some("5"))); - let v = vec!["1", "2", "3", "4", "5"]; + let v = ["1", "2", "3", "4", "5"]; let max: Result<Option<_>, <usize as std::str::FromStr>::Err> = v.into_iter().try_reduce(|x, y| { if x.parse::<usize>()? > y.parse::<usize>()? { Ok(x) } else { Ok(y) } diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index ec700346ac9..841c114063d 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -15,6 +15,7 @@ #![feature(const_convert)] #![feature(const_maybe_uninit_as_mut_ptr)] #![feature(const_maybe_uninit_assume_init)] +#![feature(const_maybe_uninit_assume_init_read)] #![feature(const_num_from_num)] #![feature(const_ptr_read)] #![feature(const_ptr_write)] @@ -46,7 +47,6 @@ #![feature(slice_take)] #![feature(maybe_uninit_uninit_array)] #![feature(maybe_uninit_array_assume_init)] -#![feature(maybe_uninit_extra)] #![feature(maybe_uninit_write_slice)] #![feature(min_specialization)] #![feature(numfmt)] diff --git a/library/core/tests/num/bignum.rs b/library/core/tests/num/bignum.rs index 1457064cc8d..416e7cea7a6 100644 --- a/library/core/tests/num/bignum.rs +++ b/library/core/tests/num/bignum.rs @@ -1,4 +1,5 @@ use core::num::bignum::tests::Big8x3 as Big; +use core::num::bignum::Big32x40; #[test] #[should_panic] @@ -215,6 +216,16 @@ fn test_get_bit_out_of_range() { #[test] fn test_bit_length() { + for i in 0..8 * 3 { + // 010000...000 + assert_eq!(Big::from_small(1).mul_pow2(i).bit_length(), i + 1); + } + for i in 1..8 * 3 - 1 { + // 010000...001 + assert_eq!(Big::from_small(1).mul_pow2(i).add(&Big::from_small(1)).bit_length(), i + 1); + // 110000...000 + assert_eq!(Big::from_small(3).mul_pow2(i).bit_length(), i + 2); + } assert_eq!(Big::from_small(0).bit_length(), 0); assert_eq!(Big::from_small(1).bit_length(), 1); assert_eq!(Big::from_small(5).bit_length(), 3); @@ -224,6 +235,30 @@ fn test_bit_length() { } #[test] +fn test_bit_length_32x40() { + for i in 0..32 * 40 { + // 010000...000 + assert_eq!(Big32x40::from_small(1).mul_pow2(i).bit_length(), i + 1); + } + for i in 1..32 * 40 - 1 { + // 010000...001 + assert_eq!( + Big32x40::from_small(1).mul_pow2(i).add(&Big32x40::from_small(1)).bit_length(), + i + 1 + ); + // 110000...000 + assert_eq!(Big32x40::from_small(3).mul_pow2(i).bit_length(), i + 2); + } + assert_eq!(Big32x40::from_small(0).bit_length(), 0); + assert_eq!(Big32x40::from_small(1).bit_length(), 1); + assert_eq!(Big32x40::from_small(5).bit_length(), 3); + assert_eq!(Big32x40::from_small(0x18).bit_length(), 5); + assert_eq!(Big32x40::from_u64(0x4073).bit_length(), 15); + assert_eq!(Big32x40::from_u64(0xffffff).bit_length(), 24); + assert_eq!(Big32x40::from_u64(0xffffffffffffffff).bit_length(), 64); +} + +#[test] fn test_ord() { assert!(Big::from_u64(0) < Big::from_u64(0xffffff)); assert!(Big::from_u64(0x102) < Big::from_u64(0x201)); diff --git a/library/panic_abort/Cargo.toml b/library/panic_abort/Cargo.toml index 6dec0e67497..46183d1ad00 100644 --- a/library/panic_abort/Cargo.toml +++ b/library/panic_abort/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust.git" description = "Implementation of Rust panics via process aborts" -edition = "2018" +edition = "2021" [lib] test = false diff --git a/library/panic_unwind/Cargo.toml b/library/panic_unwind/Cargo.toml index 67405463aa6..d720cc7bcbd 100644 --- a/library/panic_unwind/Cargo.toml +++ b/library/panic_unwind/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust.git" description = "Implementation of Rust panics via stack unwinding" -edition = "2018" +edition = "2021" [lib] test = false diff --git a/library/proc_macro/Cargo.toml b/library/proc_macro/Cargo.toml index faf460e32bd..db5e2e4e245 100644 --- a/library/proc_macro/Cargo.toml +++ b/library/proc_macro/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "proc_macro" version = "0.0.0" -edition = "2018" +edition = "2021" [dependencies] std = { path = "../std" } diff --git a/library/profiler_builtins/Cargo.toml b/library/profiler_builtins/Cargo.toml index 0f7f0067652..3371dfa1242 100644 --- a/library/profiler_builtins/Cargo.toml +++ b/library/profiler_builtins/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "profiler_builtins" version = "0.0.0" -edition = "2018" +edition = "2021" [lib] test = false diff --git a/library/rustc-std-workspace-alloc/Cargo.toml b/library/rustc-std-workspace-alloc/Cargo.toml index 1ea421834a7..049ca3e46b5 100644 --- a/library/rustc-std-workspace-alloc/Cargo.toml +++ b/library/rustc-std-workspace-alloc/Cargo.toml @@ -5,7 +5,7 @@ license = 'MIT OR Apache-2.0' description = """ Hack for the compiler's own build system """ -edition = "2018" +edition = "2021" [lib] path = "lib.rs" diff --git a/library/rustc-std-workspace-core/Cargo.toml b/library/rustc-std-workspace-core/Cargo.toml index 01e8b92e149..ff5cfcbd641 100644 --- a/library/rustc-std-workspace-core/Cargo.toml +++ b/library/rustc-std-workspace-core/Cargo.toml @@ -5,7 +5,7 @@ license = 'MIT OR Apache-2.0' description = """ Hack for the compiler's own build system """ -edition = "2018" +edition = "2021" [lib] path = "lib.rs" diff --git a/library/rustc-std-workspace-std/Cargo.toml b/library/rustc-std-workspace-std/Cargo.toml index 811bc78d210..3a1dc2a02b5 100644 --- a/library/rustc-std-workspace-std/Cargo.toml +++ b/library/rustc-std-workspace-std/Cargo.toml @@ -5,7 +5,7 @@ license = 'MIT OR Apache-2.0' description = """ Hack for the compiler's own build system """ -edition = "2018" +edition = "2021" [lib] path = "lib.rs" diff --git a/library/std/src/collections/hash/map/tests.rs b/library/std/src/collections/hash/map/tests.rs index d9b20aee2d2..eac884bfe0f 100644 --- a/library/std/src/collections/hash/map/tests.rs +++ b/library/std/src/collections/hash/map/tests.rs @@ -420,8 +420,8 @@ fn test_iterate() { #[test] fn test_keys() { - let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')]; - let map: HashMap<_, _> = vec.into_iter().collect(); + let pairs = [(1, 'a'), (2, 'b'), (3, 'c')]; + let map: HashMap<_, _> = pairs.into_iter().collect(); let keys: Vec<_> = map.keys().cloned().collect(); assert_eq!(keys.len(), 3); assert!(keys.contains(&1)); @@ -431,8 +431,8 @@ fn test_keys() { #[test] fn test_values() { - let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')]; - let map: HashMap<_, _> = vec.into_iter().collect(); + let pairs = [(1, 'a'), (2, 'b'), (3, 'c')]; + let map: HashMap<_, _> = pairs.into_iter().collect(); let values: Vec<_> = map.values().cloned().collect(); assert_eq!(values.len(), 3); assert!(values.contains(&'a')); @@ -442,8 +442,8 @@ fn test_values() { #[test] fn test_values_mut() { - let vec = vec![(1, 1), (2, 2), (3, 3)]; - let mut map: HashMap<_, _> = vec.into_iter().collect(); + let pairs = [(1, 1), (2, 2), (3, 3)]; + let mut map: HashMap<_, _> = pairs.into_iter().collect(); for value in map.values_mut() { *value = (*value) * 2 } @@ -456,8 +456,8 @@ fn test_values_mut() { #[test] fn test_into_keys() { - let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')]; - let map: HashMap<_, _> = vec.into_iter().collect(); + let pairs = [(1, 'a'), (2, 'b'), (3, 'c')]; + let map: HashMap<_, _> = pairs.into_iter().collect(); let keys: Vec<_> = map.into_keys().collect(); assert_eq!(keys.len(), 3); @@ -468,8 +468,8 @@ fn test_into_keys() { #[test] fn test_into_values() { - let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')]; - let map: HashMap<_, _> = vec.into_iter().collect(); + let pairs = [(1, 'a'), (2, 'b'), (3, 'c')]; + let map: HashMap<_, _> = pairs.into_iter().collect(); let values: Vec<_> = map.into_values().collect(); assert_eq!(values.len(), 3); diff --git a/library/std/src/collections/mod.rs b/library/std/src/collections/mod.rs index 8b004525b46..b5e81deb480 100644 --- a/library/std/src/collections/mod.rs +++ b/library/std/src/collections/mod.rs @@ -232,7 +232,7 @@ //! ``` //! use std::collections::VecDeque; //! -//! let vec = vec![1, 2, 3, 4]; +//! let vec = [1, 2, 3, 4]; //! let buf: VecDeque<_> = vec.into_iter().collect(); //! ``` //! diff --git a/library/std/src/error.rs b/library/std/src/error.rs index 6ae0bc47a94..643108b88bf 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -25,7 +25,7 @@ use crate::backtrace::Backtrace; use crate::borrow::Cow; use crate::cell; use crate::char; -use crate::fmt::{self, Debug, Display}; +use crate::fmt::{self, Debug, Display, Write}; use crate::mem::transmute; use crate::num; use crate::str; @@ -63,7 +63,7 @@ pub trait Error: Debug + Display { /// /// #[derive(Debug)] /// struct SuperError { - /// side: SuperErrorSideKick, + /// source: SuperErrorSideKick, /// } /// /// impl fmt::Display for SuperError { @@ -74,7 +74,7 @@ pub trait Error: Debug + Display { /// /// impl Error for SuperError { /// fn source(&self) -> Option<&(dyn Error + 'static)> { - /// Some(&self.side) + /// Some(&self.source) /// } /// } /// @@ -90,7 +90,7 @@ pub trait Error: Debug + Display { /// impl Error for SuperErrorSideKick {} /// /// fn get_super_error() -> Result<(), SuperError> { - /// Err(SuperError { side: SuperErrorSideKick }) + /// Err(SuperError { source: SuperErrorSideKick }) /// } /// /// fn main() { @@ -478,6 +478,9 @@ impl Error for char::DecodeUtf16Error { } } +#[stable(feature = "u8_from_char", since = "1.59.0")] +impl Error for char::TryFromCharError {} + #[unstable(feature = "map_try_insert", issue = "82766")] impl<'a, K: Debug + Ord, V: Debug> Error for crate::collections::btree_map::OccupiedError<'a, K, V> @@ -603,21 +606,21 @@ impl Error for time::FromSecsError {} // Copied from `any.rs`. impl dyn Error + 'static { - /// Returns `true` if the boxed type is the same as `T` + /// Returns `true` if the inner type is the same as `T`. #[stable(feature = "error_downcast", since = "1.3.0")] #[inline] pub fn is<T: Error + 'static>(&self) -> bool { // Get `TypeId` of the type this function is instantiated with. let t = TypeId::of::<T>(); - // Get `TypeId` of the type in the trait object. - let boxed = self.type_id(private::Internal); + // Get `TypeId` of the type in the trait object (`self`). + let concrete = self.type_id(private::Internal); // Compare both `TypeId`s on equality. - t == boxed + t == concrete } - /// Returns some reference to the boxed value if it is of type `T`, or + /// Returns some reference to the inner value if it is of type `T`, or /// `None` if it isn't. #[stable(feature = "error_downcast", since = "1.3.0")] #[inline] @@ -629,7 +632,7 @@ impl dyn Error + 'static { } } - /// Returns some mutable reference to the boxed value if it is of type `T`, or + /// Returns some mutable reference to the inner value if it is of type `T`, or /// `None` if it isn't. #[stable(feature = "error_downcast", since = "1.3.0")] #[inline] @@ -807,3 +810,642 @@ impl dyn Error + Send + Sync { }) } } + +/// An error reporter that print's an error and its sources. +/// +/// Report also exposes configuration options for formatting the error chain, either entirely on a +/// single line, or in multi-line format with each cause in the error chain on a new line. +/// +/// `Report` only requires that the wrapped error implements `Error`. It doesn't require that the +/// wrapped error be `Send`, `Sync`, or `'static`. +/// +/// # Examples +/// +/// ```rust +/// #![feature(error_reporter)] +/// use std::error::{Error, Report}; +/// use std::fmt; +/// +/// #[derive(Debug)] +/// struct SuperError { +/// source: SuperErrorSideKick, +/// } +/// +/// impl fmt::Display for SuperError { +/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// write!(f, "SuperError is here!") +/// } +/// } +/// +/// impl Error for SuperError { +/// fn source(&self) -> Option<&(dyn Error + 'static)> { +/// Some(&self.source) +/// } +/// } +/// +/// #[derive(Debug)] +/// struct SuperErrorSideKick; +/// +/// impl fmt::Display for SuperErrorSideKick { +/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// write!(f, "SuperErrorSideKick is here!") +/// } +/// } +/// +/// impl Error for SuperErrorSideKick {} +/// +/// fn get_super_error() -> Result<(), SuperError> { +/// Err(SuperError { source: SuperErrorSideKick }) +/// } +/// +/// fn main() { +/// match get_super_error() { +/// Err(e) => println!("Error: {}", Report::new(e)), +/// _ => println!("No error"), +/// } +/// } +/// ``` +/// +/// This example produces the following output: +/// +/// ```console +/// Error: SuperError is here!: SuperErrorSideKick is here! +/// ``` +/// +/// ## Output consistency +/// +/// Report prints the same output via `Display` and `Debug`, so it works well with +/// [`Result::unwrap`]/[`Result::expect`] which print their `Err` variant via `Debug`: +/// +/// ```should_panic +/// #![feature(error_reporter)] +/// use std::error::Report; +/// # use std::error::Error; +/// # use std::fmt; +/// # #[derive(Debug)] +/// # struct SuperError { +/// # source: SuperErrorSideKick, +/// # } +/// # impl fmt::Display for SuperError { +/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// # write!(f, "SuperError is here!") +/// # } +/// # } +/// # impl Error for SuperError { +/// # fn source(&self) -> Option<&(dyn Error + 'static)> { +/// # Some(&self.source) +/// # } +/// # } +/// # #[derive(Debug)] +/// # struct SuperErrorSideKick; +/// # impl fmt::Display for SuperErrorSideKick { +/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// # write!(f, "SuperErrorSideKick is here!") +/// # } +/// # } +/// # impl Error for SuperErrorSideKick {} +/// # fn get_super_error() -> Result<(), SuperError> { +/// # Err(SuperError { source: SuperErrorSideKick }) +/// # } +/// +/// get_super_error().map_err(Report::new).unwrap(); +/// ``` +/// +/// This example produces the following output: +/// +/// ```console +/// thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: SuperError is here!: SuperErrorSideKick is here!', src/error.rs:34:40 +/// note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +/// ``` +/// +/// ## Return from `main` +/// +/// `Report` also implements `From` for all types that implement [`Error`], this when combined with +/// the `Debug` output means `Report` is an ideal starting place for formatting errors returned +/// from `main`. +/// +/// ```should_panic +/// #![feature(error_reporter)] +/// use std::error::Report; +/// # use std::error::Error; +/// # use std::fmt; +/// # #[derive(Debug)] +/// # struct SuperError { +/// # source: SuperErrorSideKick, +/// # } +/// # impl fmt::Display for SuperError { +/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// # write!(f, "SuperError is here!") +/// # } +/// # } +/// # impl Error for SuperError { +/// # fn source(&self) -> Option<&(dyn Error + 'static)> { +/// # Some(&self.source) +/// # } +/// # } +/// # #[derive(Debug)] +/// # struct SuperErrorSideKick; +/// # impl fmt::Display for SuperErrorSideKick { +/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// # write!(f, "SuperErrorSideKick is here!") +/// # } +/// # } +/// # impl Error for SuperErrorSideKick {} +/// # fn get_super_error() -> Result<(), SuperError> { +/// # Err(SuperError { source: SuperErrorSideKick }) +/// # } +/// +/// fn main() -> Result<(), Report> { +/// get_super_error()?; +/// Ok(()) +/// } +/// ``` +/// +/// This example produces the following output: +/// +/// ```console +/// Error: SuperError is here!: SuperErrorSideKick is here! +/// ``` +/// +/// **Note**: `Report`s constructed via `?` and `From` will be configured to use the single line +/// output format, if you want to make sure your `Report`s are pretty printed and include backtrace +/// you will need to manually convert and enable those flags. +/// +/// ```should_panic +/// #![feature(error_reporter)] +/// use std::error::Report; +/// # use std::error::Error; +/// # use std::fmt; +/// # #[derive(Debug)] +/// # struct SuperError { +/// # source: SuperErrorSideKick, +/// # } +/// # impl fmt::Display for SuperError { +/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// # write!(f, "SuperError is here!") +/// # } +/// # } +/// # impl Error for SuperError { +/// # fn source(&self) -> Option<&(dyn Error + 'static)> { +/// # Some(&self.source) +/// # } +/// # } +/// # #[derive(Debug)] +/// # struct SuperErrorSideKick; +/// # impl fmt::Display for SuperErrorSideKick { +/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// # write!(f, "SuperErrorSideKick is here!") +/// # } +/// # } +/// # impl Error for SuperErrorSideKick {} +/// # fn get_super_error() -> Result<(), SuperError> { +/// # Err(SuperError { source: SuperErrorSideKick }) +/// # } +/// +/// fn main() -> Result<(), Report> { +/// get_super_error() +/// .map_err(Report::from) +/// .map_err(|r| r.pretty(true).show_backtrace(true))?; +/// Ok(()) +/// } +/// ``` +/// +/// This example produces the following output: +/// +/// ```console +/// Error: SuperError is here! +/// +/// Caused by: +/// SuperErrorSideKick is here! +/// ``` +#[unstable(feature = "error_reporter", issue = "90172")] +pub struct Report<E = Box<dyn Error>> { + /// The error being reported. + error: E, + /// Whether a backtrace should be included as part of the report. + show_backtrace: bool, + /// Whether the report should be pretty-printed. + pretty: bool, +} + +impl<E> Report<E> +where + Report<E>: From<E>, +{ + /// Create a new `Report` from an input error. + #[unstable(feature = "error_reporter", issue = "90172")] + pub fn new(error: E) -> Report<E> { + Self::from(error) + } +} + +impl<E> Report<E> { + /// Enable pretty-printing the report across multiple lines. + /// + /// # Examples + /// + /// ```rust + /// #![feature(error_reporter)] + /// use std::error::Report; + /// # use std::error::Error; + /// # use std::fmt; + /// # #[derive(Debug)] + /// # struct SuperError { + /// # source: SuperErrorSideKick, + /// # } + /// # impl fmt::Display for SuperError { + /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// # write!(f, "SuperError is here!") + /// # } + /// # } + /// # impl Error for SuperError { + /// # fn source(&self) -> Option<&(dyn Error + 'static)> { + /// # Some(&self.source) + /// # } + /// # } + /// # #[derive(Debug)] + /// # struct SuperErrorSideKick; + /// # impl fmt::Display for SuperErrorSideKick { + /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// # write!(f, "SuperErrorSideKick is here!") + /// # } + /// # } + /// # impl Error for SuperErrorSideKick {} + /// + /// let error = SuperError { source: SuperErrorSideKick }; + /// let report = Report::new(error).pretty(true); + /// eprintln!("Error: {:?}", report); + /// ``` + /// + /// This example produces the following output: + /// + /// ```console + /// Error: SuperError is here! + /// + /// Caused by: + /// SuperErrorSideKick is here! + /// ``` + /// + /// When there are multiple source errors the causes will be numbered in order of iteration + /// starting from the outermost error. + /// + /// ```rust + /// #![feature(error_reporter)] + /// use std::error::Report; + /// # use std::error::Error; + /// # use std::fmt; + /// # #[derive(Debug)] + /// # struct SuperError { + /// # source: SuperErrorSideKick, + /// # } + /// # impl fmt::Display for SuperError { + /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// # write!(f, "SuperError is here!") + /// # } + /// # } + /// # impl Error for SuperError { + /// # fn source(&self) -> Option<&(dyn Error + 'static)> { + /// # Some(&self.source) + /// # } + /// # } + /// # #[derive(Debug)] + /// # struct SuperErrorSideKick { + /// # source: SuperErrorSideKickSideKick, + /// # } + /// # impl fmt::Display for SuperErrorSideKick { + /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// # write!(f, "SuperErrorSideKick is here!") + /// # } + /// # } + /// # impl Error for SuperErrorSideKick { + /// # fn source(&self) -> Option<&(dyn Error + 'static)> { + /// # Some(&self.source) + /// # } + /// # } + /// # #[derive(Debug)] + /// # struct SuperErrorSideKickSideKick; + /// # impl fmt::Display for SuperErrorSideKickSideKick { + /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// # write!(f, "SuperErrorSideKickSideKick is here!") + /// # } + /// # } + /// # impl Error for SuperErrorSideKickSideKick { } + /// + /// let source = SuperErrorSideKickSideKick; + /// let source = SuperErrorSideKick { source }; + /// let error = SuperError { source }; + /// let report = Report::new(error).pretty(true); + /// eprintln!("Error: {:?}", report); + /// ``` + /// + /// This example produces the following output: + /// + /// ```console + /// Error: SuperError is here! + /// + /// Caused by: + /// 0: SuperErrorSideKick is here! + /// 1: SuperErrorSideKickSideKick is here! + /// ``` + #[unstable(feature = "error_reporter", issue = "90172")] + pub fn pretty(mut self, pretty: bool) -> Self { + self.pretty = pretty; + self + } + + /// Display backtrace if available when using pretty output format. + /// + /// # Examples + /// + /// **Note**: Report will search for the first `Backtrace` it can find starting from the + /// outermost error. In this example it will display the backtrace from the second error in the + /// chain, `SuperErrorSideKick`. + /// + /// ```rust + /// #![feature(error_reporter)] + /// #![feature(backtrace)] + /// # use std::error::Error; + /// # use std::fmt; + /// use std::error::Report; + /// use std::backtrace::Backtrace; + /// + /// # #[derive(Debug)] + /// # struct SuperError { + /// # source: SuperErrorSideKick, + /// # } + /// # impl fmt::Display for SuperError { + /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// # write!(f, "SuperError is here!") + /// # } + /// # } + /// # impl Error for SuperError { + /// # fn source(&self) -> Option<&(dyn Error + 'static)> { + /// # Some(&self.source) + /// # } + /// # } + /// #[derive(Debug)] + /// struct SuperErrorSideKick { + /// backtrace: Backtrace, + /// } + /// + /// impl SuperErrorSideKick { + /// fn new() -> SuperErrorSideKick { + /// SuperErrorSideKick { backtrace: Backtrace::force_capture() } + /// } + /// } + /// + /// impl Error for SuperErrorSideKick { + /// fn backtrace(&self) -> Option<&Backtrace> { + /// Some(&self.backtrace) + /// } + /// } + /// + /// // The rest of the example is unchanged ... + /// # impl fmt::Display for SuperErrorSideKick { + /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// # write!(f, "SuperErrorSideKick is here!") + /// # } + /// # } + /// + /// let source = SuperErrorSideKick::new(); + /// let error = SuperError { source }; + /// let report = Report::new(error).pretty(true).show_backtrace(true); + /// eprintln!("Error: {:?}", report); + /// ``` + /// + /// This example produces something similar to the following output: + /// + /// ```console + /// Error: SuperError is here! + /// + /// Caused by: + /// SuperErrorSideKick is here! + /// + /// Stack backtrace: + /// 0: rust_out::main::_doctest_main_src_error_rs_1158_0::SuperErrorSideKick::new + /// 1: rust_out::main::_doctest_main_src_error_rs_1158_0 + /// 2: rust_out::main + /// 3: core::ops::function::FnOnce::call_once + /// 4: std::sys_common::backtrace::__rust_begin_short_backtrace + /// 5: std::rt::lang_start::{{closure}} + /// 6: std::panicking::try + /// 7: std::rt::lang_start_internal + /// 8: std::rt::lang_start + /// 9: main + /// 10: __libc_start_main + /// 11: _start + /// ``` + #[unstable(feature = "error_reporter", issue = "90172")] + pub fn show_backtrace(mut self, show_backtrace: bool) -> Self { + self.show_backtrace = show_backtrace; + self + } +} + +impl<E> Report<E> +where + E: Error, +{ + fn backtrace(&self) -> Option<&Backtrace> { + // have to grab the backtrace on the first error directly since that error may not be + // 'static + let backtrace = self.error.backtrace(); + let backtrace = backtrace.or_else(|| { + self.error + .source() + .map(|source| source.chain().find_map(|source| source.backtrace())) + .flatten() + }); + backtrace + } + + /// Format the report as a single line. + #[unstable(feature = "error_reporter", issue = "90172")] + fn fmt_singleline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.error)?; + + let sources = self.error.source().into_iter().flat_map(<dyn Error>::chain); + + for cause in sources { + write!(f, ": {}", cause)?; + } + + Ok(()) + } + + /// Format the report as multiple lines, with each error cause on its own line. + #[unstable(feature = "error_reporter", issue = "90172")] + fn fmt_multiline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let error = &self.error; + + write!(f, "{}", error)?; + + if let Some(cause) = error.source() { + write!(f, "\n\nCaused by:")?; + + let multiple = cause.source().is_some(); + + for (ind, error) in cause.chain().enumerate() { + writeln!(f)?; + let mut indented = Indented { inner: f }; + if multiple { + write!(indented, "{: >4}: {}", ind, error)?; + } else { + write!(indented, " {}", error)?; + } + } + } + + if self.show_backtrace { + let backtrace = self.backtrace(); + + if let Some(backtrace) = backtrace { + let backtrace = backtrace.to_string(); + + f.write_str("\n\nStack backtrace:\n")?; + f.write_str(backtrace.trim_end())?; + } + } + + Ok(()) + } +} + +impl Report<Box<dyn Error>> { + fn backtrace(&self) -> Option<&Backtrace> { + // have to grab the backtrace on the first error directly since that error may not be + // 'static + let backtrace = self.error.backtrace(); + let backtrace = backtrace.or_else(|| { + self.error + .source() + .map(|source| source.chain().find_map(|source| source.backtrace())) + .flatten() + }); + backtrace + } + + /// Format the report as a single line. + #[unstable(feature = "error_reporter", issue = "90172")] + fn fmt_singleline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.error)?; + + let sources = self.error.source().into_iter().flat_map(<dyn Error>::chain); + + for cause in sources { + write!(f, ": {}", cause)?; + } + + Ok(()) + } + + /// Format the report as multiple lines, with each error cause on its own line. + #[unstable(feature = "error_reporter", issue = "90172")] + fn fmt_multiline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let error = &self.error; + + write!(f, "{}", error)?; + + if let Some(cause) = error.source() { + write!(f, "\n\nCaused by:")?; + + let multiple = cause.source().is_some(); + + for (ind, error) in cause.chain().enumerate() { + writeln!(f)?; + let mut indented = Indented { inner: f }; + if multiple { + write!(indented, "{: >4}: {}", ind, error)?; + } else { + write!(indented, " {}", error)?; + } + } + } + + if self.show_backtrace { + let backtrace = self.backtrace(); + + if let Some(backtrace) = backtrace { + let backtrace = backtrace.to_string(); + + f.write_str("\n\nStack backtrace:\n")?; + f.write_str(backtrace.trim_end())?; + } + } + + Ok(()) + } +} + +#[unstable(feature = "error_reporter", issue = "90172")] +impl<E> From<E> for Report<E> +where + E: Error, +{ + fn from(error: E) -> Self { + Report { error, show_backtrace: false, pretty: false } + } +} + +#[unstable(feature = "error_reporter", issue = "90172")] +impl<'a, E> From<E> for Report<Box<dyn Error + 'a>> +where + E: Error + 'a, +{ + fn from(error: E) -> Self { + let error = box error; + Report { error, show_backtrace: false, pretty: false } + } +} + +#[unstable(feature = "error_reporter", issue = "90172")] +impl<E> fmt::Display for Report<E> +where + E: Error, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.pretty { self.fmt_multiline(f) } else { self.fmt_singleline(f) } + } +} + +#[unstable(feature = "error_reporter", issue = "90172")] +impl fmt::Display for Report<Box<dyn Error>> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.pretty { self.fmt_multiline(f) } else { self.fmt_singleline(f) } + } +} + +// This type intentionally outputs the same format for `Display` and `Debug`for +// situations where you unwrap a `Report` or return it from main. +#[unstable(feature = "error_reporter", issue = "90172")] +impl<E> fmt::Debug for Report<E> +where + Report<E>: fmt::Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +/// Wrapper type for indenting the inner source. +struct Indented<'a, D> { + inner: &'a mut D, +} + +impl<T> Write for Indented<'_, T> +where + T: Write, +{ + fn write_str(&mut self, s: &str) -> fmt::Result { + for (i, line) in s.split('\n').enumerate() { + if i > 0 { + self.inner.write_char('\n')?; + self.inner.write_str(" ")?; + } + + self.inner.write_str(line)?; + } + + Ok(()) + } +} diff --git a/library/std/src/error/tests.rs b/library/std/src/error/tests.rs index 66d6924f34d..eae5f43ff3c 100644 --- a/library/std/src/error/tests.rs +++ b/library/std/src/error/tests.rs @@ -35,3 +35,408 @@ fn downcasting() { Err(e) => assert_eq!(*e.downcast::<A>().unwrap(), A), } } + +use crate::backtrace::Backtrace; +use crate::error::Report; + +#[derive(Debug)] +struct SuperError { + source: SuperErrorSideKick, +} + +impl fmt::Display for SuperError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SuperError is here!") + } +} + +impl Error for SuperError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + Some(&self.source) + } +} + +#[derive(Debug)] +struct SuperErrorSideKick; + +impl fmt::Display for SuperErrorSideKick { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SuperErrorSideKick is here!") + } +} + +impl Error for SuperErrorSideKick {} + +#[test] +fn single_line_formatting() { + let error = SuperError { source: SuperErrorSideKick }; + let report = Report::new(&error); + let actual = report.to_string(); + let expected = String::from("SuperError is here!: SuperErrorSideKick is here!"); + + assert_eq!(expected, actual); +} + +#[test] +fn multi_line_formatting() { + let error = SuperError { source: SuperErrorSideKick }; + let report = Report::new(&error).pretty(true); + let actual = report.to_string(); + let expected = String::from( + "\ +SuperError is here! + +Caused by: + SuperErrorSideKick is here!", + ); + + assert_eq!(expected, actual); +} + +#[test] +fn error_with_no_sources_formats_single_line_correctly() { + let report = Report::new(SuperErrorSideKick); + let actual = report.to_string(); + let expected = String::from("SuperErrorSideKick is here!"); + + assert_eq!(expected, actual); +} + +#[test] +fn error_with_no_sources_formats_multi_line_correctly() { + let report = Report::new(SuperErrorSideKick).pretty(true); + let actual = report.to_string(); + let expected = String::from("SuperErrorSideKick is here!"); + + assert_eq!(expected, actual); +} + +#[test] +fn error_with_backtrace_outputs_correctly_with_one_source() { + let trace = Backtrace::force_capture(); + let expected = format!( + "\ +The source of the error + +Caused by: + Error with backtrace + +Stack backtrace: +{}", + trace + ); + let error = GenericError::new("Error with backtrace"); + let mut error = GenericError::new_with_source("The source of the error", error); + error.backtrace = Some(trace); + let report = Report::new(error).pretty(true).show_backtrace(true); + + println!("Error: {}", report); + assert_eq!(expected.trim_end(), report.to_string()); +} + +#[test] +fn error_with_backtrace_outputs_correctly_with_two_sources() { + let trace = Backtrace::force_capture(); + let expected = format!( + "\ +Error with two sources + +Caused by: + 0: The source of the error + 1: Error with backtrace + +Stack backtrace: +{}", + trace + ); + let mut error = GenericError::new("Error with backtrace"); + error.backtrace = Some(trace); + let error = GenericError::new_with_source("The source of the error", error); + let error = GenericError::new_with_source("Error with two sources", error); + let report = Report::new(error).pretty(true).show_backtrace(true); + + println!("Error: {}", report); + assert_eq!(expected.trim_end(), report.to_string()); +} + +#[derive(Debug)] +struct GenericError<D> { + message: D, + backtrace: Option<Backtrace>, + source: Option<Box<dyn Error + 'static>>, +} + +impl<D> GenericError<D> { + fn new(message: D) -> GenericError<D> { + Self { message, backtrace: None, source: None } + } + + fn new_with_source<E>(message: D, source: E) -> GenericError<D> + where + E: Error + 'static, + { + let source: Box<dyn Error + 'static> = Box::new(source); + let source = Some(source); + GenericError { message, backtrace: None, source } + } +} + +impl<D> fmt::Display for GenericError<D> +where + D: fmt::Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.message, f) + } +} + +impl<D> Error for GenericError<D> +where + D: fmt::Debug + fmt::Display, +{ + fn source(&self) -> Option<&(dyn Error + 'static)> { + self.source.as_deref() + } + + fn backtrace(&self) -> Option<&Backtrace> { + self.backtrace.as_ref() + } +} + +#[test] +fn error_formats_single_line_with_rude_display_impl() { + #[derive(Debug)] + struct MyMessage; + + impl fmt::Display for MyMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("line 1\nline 2")?; + f.write_str("\nline 3\nline 4\n")?; + f.write_str("line 5\nline 6")?; + Ok(()) + } + } + + let error = GenericError::new(MyMessage); + let error = GenericError::new_with_source(MyMessage, error); + let error = GenericError::new_with_source(MyMessage, error); + let error = GenericError::new_with_source(MyMessage, error); + let report = Report::new(error); + let expected = "\ +line 1 +line 2 +line 3 +line 4 +line 5 +line 6: line 1 +line 2 +line 3 +line 4 +line 5 +line 6: line 1 +line 2 +line 3 +line 4 +line 5 +line 6: line 1 +line 2 +line 3 +line 4 +line 5 +line 6"; + + let actual = report.to_string(); + assert_eq!(expected, actual); +} + +#[test] +fn error_formats_multi_line_with_rude_display_impl() { + #[derive(Debug)] + struct MyMessage; + + impl fmt::Display for MyMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("line 1\nline 2")?; + f.write_str("\nline 3\nline 4\n")?; + f.write_str("line 5\nline 6")?; + Ok(()) + } + } + + let error = GenericError::new(MyMessage); + let error = GenericError::new_with_source(MyMessage, error); + let error = GenericError::new_with_source(MyMessage, error); + let error = GenericError::new_with_source(MyMessage, error); + let report = Report::new(error).pretty(true); + let expected = "line 1 +line 2 +line 3 +line 4 +line 5 +line 6 + +Caused by: + 0: line 1 + line 2 + line 3 + line 4 + line 5 + line 6 + 1: line 1 + line 2 + line 3 + line 4 + line 5 + line 6 + 2: line 1 + line 2 + line 3 + line 4 + line 5 + line 6"; + + let actual = report.to_string(); + assert_eq!(expected, actual); +} + +#[test] +fn errors_that_start_with_newline_formats_correctly() { + #[derive(Debug)] + struct MyMessage; + + impl fmt::Display for MyMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("\nThe message\n") + } + } + + let error = GenericError::new(MyMessage); + let error = GenericError::new_with_source(MyMessage, error); + let error = GenericError::new_with_source(MyMessage, error); + let report = Report::new(error).pretty(true); + let expected = " +The message + + +Caused by: + 0: \ +\n The message + \ +\n 1: \ +\n The message + "; + + let actual = report.to_string(); + assert_eq!(expected, actual); +} + +#[test] +fn errors_with_multiple_writes_on_same_line_dont_insert_erroneous_newlines() { + #[derive(Debug)] + struct MyMessage; + + impl fmt::Display for MyMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("The message")?; + f.write_str(" goes on")?; + f.write_str(" and on.") + } + } + + let error = GenericError::new(MyMessage); + let error = GenericError::new_with_source(MyMessage, error); + let error = GenericError::new_with_source(MyMessage, error); + let report = Report::new(error).pretty(true); + let expected = "\ +The message goes on and on. + +Caused by: + 0: The message goes on and on. + 1: The message goes on and on."; + + let actual = report.to_string(); + println!("{}", actual); + assert_eq!(expected, actual); +} + +#[test] +fn errors_with_string_interpolation_formats_correctly() { + #[derive(Debug)] + struct MyMessage(usize); + + impl fmt::Display for MyMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Got an error code: ({}). ", self.0)?; + write!(f, "What would you like to do in response?") + } + } + + let error = GenericError::new(MyMessage(10)); + let error = GenericError::new_with_source(MyMessage(20), error); + let report = Report::new(error).pretty(true); + let expected = "\ +Got an error code: (20). What would you like to do in response? + +Caused by: + Got an error code: (10). What would you like to do in response?"; + let actual = report.to_string(); + assert_eq!(expected, actual); +} + +#[test] +fn empty_lines_mid_message() { + #[derive(Debug)] + struct MyMessage; + + impl fmt::Display for MyMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("line 1\n\nline 2") + } + } + + let error = GenericError::new(MyMessage); + let error = GenericError::new_with_source(MyMessage, error); + let error = GenericError::new_with_source(MyMessage, error); + let report = Report::new(error).pretty(true); + let expected = "\ +line 1 + +line 2 + +Caused by: + 0: line 1 + \ +\n line 2 + 1: line 1 + \ +\n line 2"; + + let actual = report.to_string(); + assert_eq!(expected, actual); +} + +#[test] +fn only_one_source() { + #[derive(Debug)] + struct MyMessage; + + impl fmt::Display for MyMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("line 1\nline 2") + } + } + + let error = GenericError::new(MyMessage); + let error = GenericError::new_with_source(MyMessage, error); + let report = Report::new(error).pretty(true); + let expected = "\ +line 1 +line 2 + +Caused by: + line 1 + line 2"; + + let actual = report.to_string(); + assert_eq!(expected, actual); +} diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index dae85027b6c..a00b5e12323 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -356,9 +356,10 @@ impl File { /// open or create a file with specific options if `open()` or `create()` /// are not appropriate. /// - /// It is equivalent to `OpenOptions::new()` but allows you to write more - /// readable code. Instead of `OpenOptions::new().read(true).open("foo.txt")` - /// you can write `File::options().read(true).open("foo.txt")`. This + /// It is equivalent to `OpenOptions::new()`, but allows you to write more + /// readable code. Instead of + /// `OpenOptions::new().append(true).open("example.log")`, + /// you can write `File::options().append(true).open("example.log")`. This /// also avoids the need to import `OpenOptions`. /// /// See the [`OpenOptions::new`] function for more details. @@ -369,7 +370,7 @@ impl File { /// use std::fs::File; /// /// fn main() -> std::io::Result<()> { - /// let mut f = File::options().read(true).open("foo.txt")?; + /// let mut f = File::options().append(true).open("example.log")?; /// Ok(()) /// } /// ``` diff --git a/library/std/src/io/buffered/mod.rs b/library/std/src/io/buffered/mod.rs index 179bdf7fe55..100dab1e249 100644 --- a/library/std/src/io/buffered/mod.rs +++ b/library/std/src/io/buffered/mod.rs @@ -12,12 +12,12 @@ use crate::error; use crate::fmt; use crate::io::Error; -pub use bufreader::BufReader; -pub use bufwriter::BufWriter; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::{bufreader::BufReader, bufwriter::BufWriter, linewriter::LineWriter}; +use linewritershim::LineWriterShim; + #[stable(feature = "bufwriter_into_parts", since = "1.56.0")] pub use bufwriter::WriterPanicked; -pub use linewriter::LineWriter; -use linewritershim::LineWriterShim; /// An error returned by [`BufWriter::into_inner`] which combines an error that /// happened while writing out the buffer, and the buffered writer object diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index ecc9e91b6bd..824938ce38e 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -261,31 +261,24 @@ use crate::str; use crate::sys; use crate::sys_common::memchr; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::buffered::IntoInnerError; #[stable(feature = "bufwriter_into_parts", since = "1.56.0")] pub use self::buffered::WriterPanicked; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::buffered::{BufReader, BufWriter, LineWriter}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::copy::copy; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::cursor::Cursor; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::error::{Error, ErrorKind, Result}; #[unstable(feature = "internal_output_capture", issue = "none")] #[doc(no_inline, hidden)] pub use self::stdio::set_output_capture; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::stdio::{stderr, stdin, stdout, Stderr, Stdin, Stdout}; -#[unstable(feature = "stdio_locked", issue = "86845")] -pub use self::stdio::{stderr_locked, stdin_locked, stdout_locked}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::stdio::{StderrLock, StdinLock, StdoutLock}; #[unstable(feature = "print_internals", issue = "none")] pub use self::stdio::{_eprint, _print}; +#[unstable(feature = "stdio_locked", issue = "86845")] +pub use self::stdio::{stderr_locked, stdin_locked, stdout_locked}; #[stable(feature = "rust1", since = "1.0.0")] -pub use self::util::{empty, repeat, sink, Empty, Repeat, Sink}; +pub use self::{ + buffered::{BufReader, BufWriter, IntoInnerError, LineWriter}, + copy::copy, + cursor::Cursor, + error::{Error, ErrorKind, Result}, + stdio::{stderr, stdin, stdout, Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock}, + util::{empty, repeat, sink, Empty, Repeat, Sink}, +}; #[unstable(feature = "read_buf", issue = "78485")] pub use self::readbuf::ReadBuf; @@ -1038,14 +1031,14 @@ pub trait Read { /// /// # use std::io; /// fn main() -> io::Result<()> { -/// let stdin = io::read_to_string(&mut io::stdin())?; +/// let stdin = io::read_to_string(io::stdin())?; /// println!("Stdin was:"); /// println!("{}", stdin); /// Ok(()) /// } /// ``` #[unstable(feature = "io_read_to_string", issue = "80218")] -pub fn read_to_string<R: Read>(reader: &mut R) -> Result<String> { +pub fn read_to_string<R: Read>(mut reader: R) -> Result<String> { let mut buf = String::new(); reader.read_to_string(&mut buf)?; Ok(buf) diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index a370485102e..35d230eee96 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -2172,7 +2172,7 @@ mod use_keyword {} /// i.next().unwrap_or_else(I::Item::default) /// } /// -/// assert_eq!(first_or_default(vec![1, 2, 3].into_iter()), 1); +/// assert_eq!(first_or_default([1, 2, 3].into_iter()), 1); /// assert_eq!(first_or_default(Vec::<i32>::new().into_iter()), 0); /// ``` /// diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index d5f9d20c426..1721e16f3a6 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -35,8 +35,8 @@ //! development you may want to press the `[-]` button near the top of the //! page to collapse it into a more skimmable view. //! -//! While you are looking at that `[-]` button also notice the `[src]` -//! button. Rust's API documentation comes with the source code and you are +//! While you are looking at that `[-]` button also notice the `source` +//! link. Rust's API documentation comes with the source code and you are //! encouraged to read it. The standard library source is generally high //! quality and a peek behind the curtains is often enlightening. //! @@ -297,7 +297,6 @@ #![feature(llvm_asm)] #![feature(log_syntax)] #![feature(map_try_insert)] -#![feature(maybe_uninit_extra)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_uninit_array)] #![feature(maybe_uninit_write_slice)] diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs index a0c77b648fe..2669f4dbf30 100644 --- a/library/std/src/net/mod.rs +++ b/library/std/src/net/mod.rs @@ -25,6 +25,8 @@ pub use self::addr::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; pub use self::ip::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::parser::AddrParseError; +#[unstable(feature = "tcplistener_into_incoming", issue = "88339")] +pub use self::tcp::IntoIncoming; #[stable(feature = "rust1", since = "1.0.0")] pub use self::tcp::{Incoming, TcpListener, TcpStream}; #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/os/linux/raw.rs b/library/std/src/os/linux/raw.rs index cd92dcabdf5..d78049bce24 100644 --- a/library/std/src/os/linux/raw.rs +++ b/library/std/src/os/linux/raw.rs @@ -239,6 +239,7 @@ mod arch { target_arch = "riscv32" ))] mod arch { + #[stable(feature = "raw_ext", since = "1.1.0")] pub use libc::{blkcnt_t, blksize_t, ino_t, nlink_t, off_t, stat, time_t}; } diff --git a/library/std/src/os/unix/ffi/os_str.rs b/library/std/src/os/unix/ffi/os_str.rs index 54c9a9382f2..650f712bc6e 100644 --- a/library/std/src/os/unix/ffi/os_str.rs +++ b/library/std/src/os/unix/ffi/os_str.rs @@ -28,9 +28,11 @@ pub trait OsStringExt: Sealed { #[stable(feature = "rust1", since = "1.0.0")] impl OsStringExt for OsString { + #[inline] fn from_vec(vec: Vec<u8>) -> OsString { FromInner::from_inner(Buf { inner: vec }) } + #[inline] fn into_vec(self) -> Vec<u8> { self.into_inner().inner } diff --git a/library/std/src/os/unix/io/raw.rs b/library/std/src/os/unix/io/raw.rs index 6317e317471..a4d2ba797d9 100644 --- a/library/std/src/os/unix/io/raw.rs +++ b/library/std/src/os/unix/io/raw.rs @@ -2,4 +2,5 @@ #![stable(feature = "rust1", since = "1.0.0")] +#[stable(feature = "rust1", since = "1.0.0")] pub use crate::os::fd::raw::*; diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 9f7f10d0d00..546f8a15b70 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -498,12 +498,12 @@ impl Builder { // exist after the thread has terminated, which is signaled by `Thread::join` // returning. native: unsafe { - Some(imp::Thread::new( + imp::Thread::new( stack_size, mem::transmute::<Box<dyn FnOnce() + 'a>, Box<dyn FnOnce() + 'static>>( Box::new(main), ), - )?) + )? }, thread: my_thread, packet: Packet(my_packet), @@ -1261,15 +1261,15 @@ unsafe impl<T: Sync> Sync for Packet<T> {} /// Inner representation for JoinHandle struct JoinInner<T> { - native: Option<imp::Thread>, + native: imp::Thread, thread: Thread, packet: Packet<T>, } impl<T> JoinInner<T> { - fn join(&mut self) -> Result<T> { - self.native.take().unwrap().join(); - unsafe { (*self.packet.0.get()).take().unwrap() } + fn join(mut self) -> Result<T> { + self.native.join(); + Arc::get_mut(&mut self.packet.0).unwrap().get_mut().take().unwrap() } } @@ -1400,7 +1400,7 @@ impl<T> JoinHandle<T> { /// join_handle.join().expect("Couldn't join on the associated thread"); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn join(mut self) -> Result<T> { + pub fn join(self) -> Result<T> { self.0.join() } @@ -1416,13 +1416,13 @@ impl<T> JoinHandle<T> { impl<T> AsInner<imp::Thread> for JoinHandle<T> { fn as_inner(&self) -> &imp::Thread { - self.0.native.as_ref().unwrap() + &self.0.native } } impl<T> IntoInner<imp::Thread> for JoinHandle<T> { fn into_inner(self) -> imp::Thread { - self.0.native.unwrap() + self.0.native } } @@ -1504,7 +1504,6 @@ fn _assert_sync_and_send() { /// /// ``` /// # #![allow(dead_code)] -/// #![feature(available_parallelism)] /// use std::{io, thread}; /// /// fn main() -> io::Result<()> { @@ -1516,7 +1515,7 @@ fn _assert_sync_and_send() { #[doc(alias = "available_concurrency")] // Alias for a previous name we gave this API on unstable. #[doc(alias = "hardware_concurrency")] // Alias for C++ `std::thread::hardware_concurrency`. #[doc(alias = "num_cpus")] // Alias for a popular ecosystem crate which provides similar functionality. -#[unstable(feature = "available_parallelism", issue = "74479")] +#[stable(feature = "available_parallelism", since = "1.59.0")] pub fn available_parallelism() -> io::Result<NonZeroUsize> { imp::available_parallelism() } diff --git a/library/std/src/time.rs b/library/std/src/time.rs index 86cc93c4453..b6867e68df7 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -54,7 +54,7 @@ pub use core::time::FromSecsError; /// instant when created, and are often useful for tasks such as measuring /// benchmarks or timing how long an operation takes. /// -/// Note, however, that instants are not guaranteed to be **steady**. In other +/// Note, however, that instants are **not** guaranteed to be **steady**. In other /// words, each tick of the underlying clock might not be the same length (e.g. /// some seconds may be longer than others). An instant may jump forwards or /// experience time dilation (slow down or speed up), but it will never go diff --git a/library/test/Cargo.toml b/library/test/Cargo.toml index 04dab6b804a..2da41484ca5 100644 --- a/library/test/Cargo.toml +++ b/library/test/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "test" version = "0.0.0" -edition = "2018" +edition = "2021" [lib] crate-type = ["dylib", "rlib"] diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 608e587cf34..fad83094cdf 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -16,7 +16,6 @@ #![unstable(feature = "test", issue = "50297")] #![doc(test(attr(deny(warnings))))] #![feature(nll)] -#![feature(available_parallelism)] #![feature(bench_black_box)] #![feature(internal_output_capture)] #![feature(staged_api)] diff --git a/library/test/src/tests.rs b/library/test/src/tests.rs index 718613895de..7f0b6193d09 100644 --- a/library/test/src/tests.rs +++ b/library/test/src/tests.rs @@ -493,7 +493,7 @@ pub fn exclude_should_panic_option() { #[test] pub fn exact_filter_match() { fn tests() -> Vec<TestDescAndFn> { - vec!["base", "base::test", "base::test1", "base::test2"] + ["base", "base::test", "base::test1", "base::test2"] .into_iter() .map(|name| TestDescAndFn { desc: TestDesc { diff --git a/library/unwind/Cargo.toml b/library/unwind/Cargo.toml index 1941f2b5a00..69fce8d7795 100644 --- a/library/unwind/Cargo.toml +++ b/library/unwind/Cargo.toml @@ -3,7 +3,7 @@ name = "unwind" version = "0.0.0" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust.git" -edition = "2018" +edition = "2021" include = [ '/libunwind/*', ] diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile index c27e42a2662..66333e2b992 100644 --- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile @@ -17,12 +17,12 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ pkg-config \ mingw-w64 -RUN curl -sL https://nodejs.org/dist/v14.4.0/node-v14.4.0-linux-x64.tar.xz | tar -xJ -ENV PATH="/node-v14.4.0-linux-x64/bin:${PATH}" +RUN curl -sL https://nodejs.org/dist/v16.9.0/node-v16.9.0-linux-x64.tar.xz | tar -xJ +ENV PATH="/node-v16.9.0-linux-x64/bin:${PATH}" # Install es-check # Pin its version to prevent unrelated CI failures due to future es-check versions. -RUN npm install es-check@5.2.3 -g -RUN npm install eslint@7.20.0 -g +RUN npm install es-check@6.1.1 -g +RUN npm install eslint@8.6.0 -g COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh @@ -40,5 +40,5 @@ ENV SCRIPT python3 ../x.py --stage 2 test src/tools/expand-yaml-anchors && \ /scripts/validate-toolstate.sh && \ /scripts/validate-error-codes.sh && \ # Runs checks to ensure that there are no ES5 issues in our JS code. - es-check es5 ../src/librustdoc/html/static/js/*.js && \ + es-check es6 ../src/librustdoc/html/static/js/*.js && \ eslint ../src/librustdoc/html/static/js/*.js diff --git a/src/ci/docker/scripts/musl-toolchain.sh b/src/ci/docker/scripts/musl-toolchain.sh index 3c17f316d1f..e358b8139d7 100644 --- a/src/ci/docker/scripts/musl-toolchain.sh +++ b/src/ci/docker/scripts/musl-toolchain.sh @@ -48,7 +48,9 @@ cd musl-cross-make git checkout a54eb56f33f255dfca60be045f12a5cfaf5a72a9 # Fix the cfi detection script in musl's configure so cfi is generated -# when debug info is asked for. +# when debug info is asked for. This patch is derived from +# https://git.musl-libc.org/cgit/musl/commit/?id=c4d4028dde90562f631edf559fbc42d8ec1b29de. +# When we upgrade to a version that includes this commit, we can remove the patch. mkdir patches/musl-1.1.24 cp ../musl-patch-configure.diff patches/musl-1.1.24/0001-fix-cfi-detection.diff diff --git a/src/doc/rustdoc/src/documentation-tests.md b/src/doc/rustdoc/src/documentation-tests.md index aea55d4f4b6..534fd19b52e 100644 --- a/src/doc/rustdoc/src/documentation-tests.md +++ b/src/doc/rustdoc/src/documentation-tests.md @@ -335,7 +335,8 @@ panic during execution. If the code doesn't panic, the test will fail. The `no_run` attribute will compile your code but not run it. This is important for examples such as "Here's how to retrieve a web page," which you would want to ensure compiles, but might be run in a test -environment that has no network access. +environment that has no network access. This attribute can also be +used to demonstrate code snippets that can cause Undefined Behavior. ```rust /// ```no_run diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index 56ca7c03928..d60be193bda 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -84,6 +84,39 @@ in documentation. `#![feature(doc_cfg)]` feature gate. For more information, see [its chapter in the Unstable Book][unstable-doc-cfg] and [its tracking issue][issue-doc-cfg]. +### `doc_auto_cfg`: Automatically generate `#[doc(cfg)]` + +`doc_auto_cfg` is an extension to the `#[doc(cfg)]` feature. With it, you don't need to add +`#[doc(cfg(...)]` anymore unless you want to override the default behaviour. So if we take the +previous source code: + +```rust +#![feature(doc_auto_cfg)] + +/// Token struct that can only be used on Windows. +#[cfg(any(windows, doc))] +pub struct WindowsToken; + +/// Token struct that can only be used on Unix. +#[cfg(any(unix, doc))] +pub struct UnixToken; + +/// Token struct that is only available with the `serde` feature +#[cfg(feature = "serde")] +#[derive(serde::Deserialize)] +pub struct SerdeToken; +``` + +It'll render almost the same, the difference being that `doc` will also be displayed. To fix this, +you can use `doc_cfg_hide`: + +```rust +#![feature(doc_cfg_hide)] +#![doc(cfg_hide(doc))] +``` + +And `doc` won't show up anymore! + [cfg-doc]: ./advanced-features.md [unstable-doc-cfg]: ../unstable-book/language-features/doc-cfg.html [issue-doc-cfg]: https://github.com/rust-lang/rust/issues/43781 diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index a3cb982f277..5025342c1d6 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -8,6 +8,7 @@ path = "lib.rs" [dependencies] arrayvec = { version = "0.7", default-features = false } +askama = { version = "0.11", default-features = false } pulldown-cmark = { version = "0.9", default-features = false } minifier = "0.0.41" rayon = "1.3.1" @@ -20,7 +21,6 @@ regex = "1" rustdoc-json-types = { path = "../rustdoc-json-types" } tracing = "0.1" tracing-tree = "0.2.0" -tera = { version = "1.10.0", default-features = false } [dependencies.tracing-subscriber] version = "0.3.3" diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index f54ab9f2b11..eafc74b9945 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -19,118 +19,119 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { trace!("get_blanket_impls({:?})", ty); let mut impls = Vec::new(); - for trait_def_id in self.cx.tcx.all_traits() { - if !self.cx.cache.access_levels.is_public(trait_def_id) - || self.cx.generated_synthetics.get(&(ty, trait_def_id)).is_some() - { - continue; - } - // NOTE: doesn't use `for_each_relevant_impl` to avoid looking at anything besides blanket impls - let trait_impls = self.cx.tcx.trait_impls_of(trait_def_id); - for &impl_def_id in trait_impls.blanket_impls() { - trace!( - "get_blanket_impls: Considering impl for trait '{:?}' {:?}", - trait_def_id, - impl_def_id - ); - let trait_ref = self.cx.tcx.impl_trait_ref(impl_def_id).unwrap(); - let is_param = matches!(trait_ref.self_ty().kind(), ty::Param(_)); - let may_apply = is_param && self.cx.tcx.infer_ctxt().enter(|infcx| { - let substs = infcx.fresh_substs_for_item(DUMMY_SP, item_def_id); - let ty = ty.subst(infcx.tcx, substs); - let param_env = param_env.subst(infcx.tcx, substs); + self.cx.with_all_traits(|cx, all_traits| { + for &trait_def_id in all_traits { + if !cx.cache.access_levels.is_public(trait_def_id) + || cx.generated_synthetics.get(&(ty, trait_def_id)).is_some() + { + continue; + } + // NOTE: doesn't use `for_each_relevant_impl` to avoid looking at anything besides blanket impls + let trait_impls = cx.tcx.trait_impls_of(trait_def_id); + for &impl_def_id in trait_impls.blanket_impls() { + trace!( + "get_blanket_impls: Considering impl for trait '{:?}' {:?}", + trait_def_id, + impl_def_id + ); + let trait_ref = cx.tcx.impl_trait_ref(impl_def_id).unwrap(); + let is_param = matches!(trait_ref.self_ty().kind(), ty::Param(_)); + let may_apply = is_param && cx.tcx.infer_ctxt().enter(|infcx| { + let substs = infcx.fresh_substs_for_item(DUMMY_SP, item_def_id); + let ty = ty.subst(infcx.tcx, substs); + let param_env = param_env.subst(infcx.tcx, substs); - let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); - let trait_ref = trait_ref.subst(infcx.tcx, impl_substs); + let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); + let trait_ref = trait_ref.subst(infcx.tcx, impl_substs); - // Require the type the impl is implemented on to match - // our type, and ignore the impl if there was a mismatch. - let cause = traits::ObligationCause::dummy(); - let eq_result = infcx.at(&cause, param_env).eq(trait_ref.self_ty(), ty); - if let Ok(InferOk { value: (), obligations }) = eq_result { - // FIXME(eddyb) ignoring `obligations` might cause false positives. - drop(obligations); + // Require the type the impl is implemented on to match + // our type, and ignore the impl if there was a mismatch. + let cause = traits::ObligationCause::dummy(); + let eq_result = infcx.at(&cause, param_env).eq(trait_ref.self_ty(), ty); + if let Ok(InferOk { value: (), obligations }) = eq_result { + // FIXME(eddyb) ignoring `obligations` might cause false positives. + drop(obligations); - trace!( - "invoking predicate_may_hold: param_env={:?}, trait_ref={:?}, ty={:?}", - param_env, - trait_ref, - ty - ); - let predicates = self - .cx - .tcx - .predicates_of(impl_def_id) - .instantiate(self.cx.tcx, impl_substs) - .predicates - .into_iter() - .chain(Some( - ty::Binder::dummy(trait_ref) - .to_poly_trait_predicate() - .map_bound(ty::PredicateKind::Trait) - .to_predicate(infcx.tcx), - )); - for predicate in predicates { - debug!("testing predicate {:?}", predicate); - let obligation = traits::Obligation::new( - traits::ObligationCause::dummy(), + trace!( + "invoking predicate_may_hold: param_env={:?}, trait_ref={:?}, ty={:?}", param_env, - predicate, + trait_ref, + ty ); - match infcx.evaluate_obligation(&obligation) { - Ok(eval_result) if eval_result.may_apply() => {} - Err(traits::OverflowError::Canonical) => {} - Err(traits::OverflowError::ErrorReporting) => {} - _ => { - return false; + let predicates = cx + .tcx + .predicates_of(impl_def_id) + .instantiate(cx.tcx, impl_substs) + .predicates + .into_iter() + .chain(Some( + ty::Binder::dummy(trait_ref) + .to_poly_trait_predicate() + .map_bound(ty::PredicateKind::Trait) + .to_predicate(infcx.tcx), + )); + for predicate in predicates { + debug!("testing predicate {:?}", predicate); + let obligation = traits::Obligation::new( + traits::ObligationCause::dummy(), + param_env, + predicate, + ); + match infcx.evaluate_obligation(&obligation) { + Ok(eval_result) if eval_result.may_apply() => {} + Err(traits::OverflowError::Canonical) => {} + Err(traits::OverflowError::ErrorReporting) => {} + _ => { + return false; + } } } + true + } else { + false } - true - } else { - false + }); + debug!( + "get_blanket_impls: found applicable impl: {} for trait_ref={:?}, ty={:?}", + may_apply, trait_ref, ty + ); + if !may_apply { + continue; } - }); - debug!( - "get_blanket_impls: found applicable impl: {} for trait_ref={:?}, ty={:?}", - may_apply, trait_ref, ty - ); - if !may_apply { - continue; - } - self.cx.generated_synthetics.insert((ty, trait_def_id)); + cx.generated_synthetics.insert((ty, trait_def_id)); - impls.push(Item { - name: None, - attrs: Default::default(), - visibility: Inherited, - def_id: ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id }, - kind: box ImplItem(Impl { - unsafety: hir::Unsafety::Normal, - generics: clean_ty_generics( - self.cx, - self.cx.tcx.generics_of(impl_def_id), - self.cx.tcx.explicit_predicates_of(impl_def_id), - ), - // FIXME(eddyb) compute both `trait_` and `for_` from - // the post-inference `trait_ref`, as it's more accurate. - trait_: Some(trait_ref.clean(self.cx)), - for_: ty.clean(self.cx), - items: self - .cx - .tcx - .associated_items(impl_def_id) - .in_definition_order() - .map(|x| x.clean(self.cx)) - .collect::<Vec<_>>(), - polarity: ty::ImplPolarity::Positive, - kind: ImplKind::Blanket(box trait_ref.self_ty().clean(self.cx)), - }), - cfg: None, - }); + impls.push(Item { + name: None, + attrs: Default::default(), + visibility: Inherited, + def_id: ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id }, + kind: box ImplItem(Impl { + unsafety: hir::Unsafety::Normal, + generics: clean_ty_generics( + cx, + cx.tcx.generics_of(impl_def_id), + cx.tcx.explicit_predicates_of(impl_def_id), + ), + // FIXME(eddyb) compute both `trait_` and `for_` from + // the post-inference `trait_ref`, as it's more accurate. + trait_: Some(trait_ref.clean(cx)), + for_: ty.clean(cx), + items: cx + .tcx + .associated_items(impl_def_id) + .in_definition_order() + .map(|x| x.clean(cx)) + .collect::<Vec<_>>(), + polarity: ty::ImplPolarity::Positive, + kind: ImplKind::Blanket(box trait_ref.self_ty().clean(cx)), + }), + cfg: None, + }); + } } - } + }); + impls } } diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index ce0ac322af9..a2e612955b3 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -9,7 +9,6 @@ use rustc_data_structures::thin_vec::ThinVec; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::definitions::DefPathData; use rustc_hir::Mutability; use rustc_metadata::creader::{CStore, LoadedMacro}; use rustc_middle::ty::{self, TyCtxt}; @@ -164,12 +163,10 @@ crate fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> Attrs<'hir> { /// These names are used later on by HTML rendering to generate things like /// source links back to the original item. crate fn record_extern_fqn(cx: &mut DocContext<'_>, did: DefId, kind: ItemType) { - let crate_name = cx.tcx.crate_name(did.krate).to_string(); + let crate_name = cx.tcx.crate_name(did.krate); - let relative = cx.tcx.def_path(did).data.into_iter().filter_map(|elem| { - // Filter out extern blocks - (elem.data != DefPathData::ForeignMod).then(|| elem.data.to_string()) - }); + let relative = + cx.tcx.def_path(did).data.into_iter().filter_map(|elem| elem.data.get_opt_name()); let fqn = if let ItemType::Macro = kind { // Check to see if it is a macro 2.0 or built-in macro if matches!( @@ -291,6 +288,7 @@ crate fn build_impls( attrs: Option<Attrs<'_>>, ret: &mut Vec<clean::Item>, ) { + let _prof_timer = cx.tcx.sess.prof.generic_activity("build_inherent_impls"); let tcx = cx.tcx; // for each implementation of an item represented by `did`, build the clean::Item for that impl @@ -338,7 +336,7 @@ crate fn build_impl( return; } - let _prof_timer = cx.tcx.sess.prof.generic_activity("build_extern_trait_impl"); + let _prof_timer = cx.tcx.sess.prof.generic_activity("build_impl"); let tcx = cx.tcx; let associated_trait = tcx.impl_trait_ref(did); @@ -516,7 +514,7 @@ fn build_module( // If we're re-exporting a re-export it may actually re-export something in // two namespaces, so the target may be listed twice. Make sure we only // visit each node at most once. - for &item in cx.tcx.item_children(did).iter() { + for &item in cx.tcx.module_children(did).iter() { if item.vis.is_public() { let res = item.res.expect_non_local(); if let Some(def_id) = res.mod_def_id() { diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index d80e79d164a..5d1e9d6754e 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1618,7 +1618,7 @@ impl Clean<Item> for hir::FieldDef<'_> { impl Clean<Item> for ty::FieldDef { fn clean(&self, cx: &mut DocContext<'_>) -> Item { - clean_field(self.did, self.ident.name, cx.tcx.type_of(self.did).clean(cx), cx) + clean_field(self.did, self.name, cx.tcx.type_of(self.did).clean(cx), cx) } } @@ -1689,7 +1689,7 @@ impl Clean<Item> for ty::VariantDef { }), }; let what_rustc_thinks = - Item::from_def_id_and_parts(self.def_id, Some(self.ident.name), VariantItem(kind), cx); + Item::from_def_id_and_parts(self.def_id, Some(self.name), VariantItem(kind), cx); // don't show `pub` for variants, which always inherit visibility Item { visibility: Inherited, ..what_rustc_thinks } } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index f0f61bb94c8..00c6e38839f 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1,6 +1,5 @@ use std::cell::RefCell; use std::default::Default; -use std::fmt::Write; use std::hash::Hash; use std::lazy::SyncOnceCell as OnceCell; use std::path::PathBuf; @@ -265,7 +264,7 @@ impl ExternalCrate { }) .collect() } else { - tcx.item_children(root).iter().map(|item| item.res).filter_map(as_keyword).collect() + tcx.module_children(root).iter().map(|item| item.res).filter_map(as_keyword).collect() } } @@ -333,7 +332,7 @@ impl ExternalCrate { }) .collect() } else { - tcx.item_children(root).iter().map(|item| item.res).filter_map(as_primitive).collect() + tcx.module_children(root).iter().map(|item| item.res).filter_map(as_primitive).collect() } } } @@ -496,7 +495,7 @@ impl Item { if let Ok((mut href, ..)) = href(*did, cx) { debug!(?href); if let Some(ref fragment) = *fragment { - write!(href, "{}", fragment).unwrap() + fragment.render(&mut href, cx.tcx()).unwrap() } Some(RenderedLink { original_text: s.clone(), @@ -1082,9 +1081,6 @@ impl Attributes { let mut out = String::new(); add_doc_fragment(&mut out, ori); for new_frag in iter { - if new_frag.kind != ori.kind || new_frag.parent_module != ori.parent_module { - break; - } add_doc_fragment(&mut out, new_frag); } out.pop(); diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 43dcb611a37..b14430d10e9 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -17,6 +17,8 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_middle::mir::interpret::ConstValue; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; use rustc_middle::ty::{self, DefIdTree, TyCtxt}; +use rustc_session::parse::ParseSess; +use rustc_span::source_map::FilePathMapping; use rustc_span::symbol::{kw, sym, Symbol}; use std::fmt::Write as _; use std::mem; @@ -179,6 +181,7 @@ crate fn build_deref_target_impls(cx: &mut DocContext<'_>, items: &[Item], ret: }; if let Some(prim) = target.primitive_type() { + let _prof_timer = cx.tcx.sess.prof.generic_activity("build_primitive_inherent_impls"); for &did in prim.impls(tcx).iter().filter(|did| !did.is_local()) { inline::build_impl(cx, None, did, None, ret); } @@ -485,20 +488,67 @@ crate const DOC_RUST_LANG_ORG_CHANNEL: &str = env!("DOC_RUST_LANG_ORG_CHANNEL"); /// Render a sequence of macro arms in a format suitable for displaying to the user /// as part of an item declaration. pub(super) fn render_macro_arms<'a>( + tcx: TyCtxt<'_>, matchers: impl Iterator<Item = &'a TokenTree>, arm_delim: &str, ) -> String { let mut out = String::new(); for matcher in matchers { - writeln!(out, " {} => {{ ... }}{}", render_macro_matcher(matcher), arm_delim).unwrap(); + writeln!(out, " {} => {{ ... }}{}", render_macro_matcher(tcx, matcher), arm_delim) + .unwrap(); } out } /// Render a macro matcher in a format suitable for displaying to the user /// as part of an item declaration. -pub(super) fn render_macro_matcher(matcher: &TokenTree) -> String { - rustc_ast_pretty::pprust::tt_to_string(matcher) +pub(super) fn render_macro_matcher(tcx: TyCtxt<'_>, matcher: &TokenTree) -> String { + if let Some(snippet) = snippet_equal_to_token(tcx, matcher) { + snippet + } else { + rustc_ast_pretty::pprust::tt_to_string(matcher) + } +} + +/// Find the source snippet for this token's Span, reparse it, and return the +/// snippet if the reparsed TokenTree matches the argument TokenTree. +fn snippet_equal_to_token(tcx: TyCtxt<'_>, matcher: &TokenTree) -> Option<String> { + // Find what rustc thinks is the source snippet. + // This may not actually be anything meaningful if this matcher was itself + // generated by a macro. + let source_map = tcx.sess.source_map(); + let span = matcher.span(); + let snippet = source_map.span_to_snippet(span).ok()?; + + // Create a Parser. + let sess = ParseSess::new(FilePathMapping::empty()); + let file_name = source_map.span_to_filename(span); + let mut parser = + match rustc_parse::maybe_new_parser_from_source_str(&sess, file_name, snippet.clone()) { + Ok(parser) => parser, + Err(diagnostics) => { + for mut diagnostic in diagnostics { + diagnostic.cancel(); + } + return None; + } + }; + + // Reparse a single token tree. + let mut reparsed_trees = match parser.parse_all_token_trees() { + Ok(reparsed_trees) => reparsed_trees, + Err(mut diagnostic) => { + diagnostic.cancel(); + return None; + } + }; + if reparsed_trees.len() != 1 { + return None; + } + let reparsed_tree = reparsed_trees.pop().unwrap(); + + // Compare against the original tree. + if reparsed_tree.eq_unspanned(matcher) { Some(snippet) } else { None } } pub(super) fn display_macro_source( @@ -513,21 +563,21 @@ pub(super) fn display_macro_source( let matchers = tts.chunks(4).map(|arm| &arm[0]); if def.macro_rules { - format!("macro_rules! {} {{\n{}}}", name, render_macro_arms(matchers, ";")) + format!("macro_rules! {} {{\n{}}}", name, render_macro_arms(cx.tcx, matchers, ";")) } else { if matchers.len() <= 1 { format!( "{}macro {}{} {{\n ...\n}}", vis.to_src_with_space(cx.tcx, def_id), name, - matchers.map(render_macro_matcher).collect::<String>(), + matchers.map(|matcher| render_macro_matcher(cx.tcx, matcher)).collect::<String>(), ) } else { format!( "{}macro {} {{\n{}}}", vis.to_src_with_space(cx.tcx, def_id), name, - render_macro_arms(matchers, ","), + render_macro_arms(cx.tcx, matchers, ","), ) } } diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index d300afa3132..959f83a0211 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -257,9 +257,6 @@ crate struct RenderOptions { /// If present, playground URL to use in the "Run" button added to code samples generated from /// standalone Markdown files. If not present, `playground_url` is used. crate markdown_playground_url: Option<String>, - /// If false, the `select` element to have search filtering by crates on rendered docs - /// won't be generated. - crate generate_search_filter: bool, /// Document items that have lower than `pub` visibility. crate document_private: bool, /// Document items that have `doc(hidden)`. @@ -638,7 +635,6 @@ impl Options { let crate_version = matches.opt_str("crate-version"); let enable_index_page = matches.opt_present("enable-index-page") || index_page.is_some(); let static_root_path = matches.opt_str("static-root-path"); - let generate_search_filter = !matches.opt_present("disable-per-crate-search"); let test_run_directory = matches.opt_str("test-run-directory").map(PathBuf::from); let persist_doctests = matches.opt_str("persist-doctests").map(PathBuf::from); let test_builder = matches.opt_str("test-builder").map(PathBuf::from); @@ -724,7 +720,6 @@ impl Options { markdown_no_toc, markdown_css, markdown_playground_url, - generate_search_filter, document_private, document_hidden, generate_redirect_map, diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 32b66278bf4..22f59d39799 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -1,30 +1,23 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::{self, Lrc}; -use rustc_driver::abort_on_err; use rustc_errors::emitter::{Emitter, EmitterWriter}; use rustc_errors::json::JsonEmitter; use rustc_feature::UnstableFeatures; use rustc_hir::def::Res; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::HirId; -use rustc_hir::{ - intravisit::{self, NestedVisitorMap, Visitor}, - Path, -}; -use rustc_interface::{interface, Queries}; +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::{HirId, Path}; +use rustc_interface::interface; use rustc_middle::hir::map::Map; use rustc_middle::middle::privacy::AccessLevels; use rustc_middle::ty::{ParamEnv, Ty, TyCtxt}; use rustc_resolve as resolve; -use rustc_resolve::Namespace::TypeNS; use rustc_session::config::{self, CrateType, ErrorOutputType}; use rustc_session::lint; use rustc_session::DiagnosticOutput; use rustc_session::Session; -use rustc_span::def_id::CRATE_DEF_INDEX; -use rustc_span::source_map; use rustc_span::symbol::sym; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::{source_map, Span}; use std::cell::RefCell; use std::lazy::SyncLazy; @@ -39,14 +32,20 @@ use crate::passes::{self, Condition::*}; crate use rustc_session::config::{DebuggingOptions, Input, Options}; +crate struct ResolverCaches { + pub all_traits: Option<Vec<DefId>>, + pub all_trait_impls: Option<Vec<DefId>>, +} + crate struct DocContext<'tcx> { crate tcx: TyCtxt<'tcx>, /// Name resolver. Used for intra-doc links. /// /// The `Rc<RefCell<...>>` wrapping is needed because that is what's returned by - /// [`Queries::expansion()`]. + /// [`rustc_interface::Queries::expansion()`]. // FIXME: see if we can get rid of this RefCell somehow crate resolver: Rc<RefCell<interface::BoxedResolver>>, + crate resolver_caches: ResolverCaches, /// Used for normalization. /// /// Most of this logic is copied from rustc_lint::late. @@ -123,6 +122,18 @@ impl<'tcx> DocContext<'tcx> { _ => None, } } + + crate fn with_all_traits(&mut self, f: impl FnOnce(&mut Self, &[DefId])) { + let all_traits = self.resolver_caches.all_traits.take(); + f(self, all_traits.as_ref().expect("`all_traits` are already borrowed")); + self.resolver_caches.all_traits = all_traits; + } + + crate fn with_all_trait_impls(&mut self, f: impl FnOnce(&mut Self, &[DefId])) { + let all_trait_impls = self.resolver_caches.all_trait_impls.take(); + f(self, all_trait_impls.as_ref().expect("`all_trait_impls` are already borrowed")); + self.resolver_caches.all_trait_impls = all_trait_impls; + } } /// Creates a new diagnostic `Handler` that can be used to emit warnings and errors. @@ -284,49 +295,10 @@ crate fn create_config( } } -crate fn create_resolver<'a>( - externs: config::Externs, - queries: &Queries<'a>, - sess: &Session, -) -> Rc<RefCell<interface::BoxedResolver>> { - let (krate, resolver, _) = &*abort_on_err(queries.expansion(), sess).peek(); - let resolver = resolver.clone(); - - let resolver = crate::passes::collect_intra_doc_links::load_intra_link_crates(resolver, krate); - - // FIXME: somehow rustdoc is still missing crates even though we loaded all - // the known necessary crates. Load them all unconditionally until we find a way to fix this. - // DO NOT REMOVE THIS without first testing on the reproducer in - // https://github.com/jyn514/objr/commit/edcee7b8124abf0e4c63873e8422ff81beb11ebb - let extern_names: Vec<String> = externs - .iter() - .filter(|(_, entry)| entry.add_prelude) - .map(|(name, _)| name) - .cloned() - .collect(); - resolver.borrow_mut().access(|resolver| { - sess.time("load_extern_crates", || { - for extern_name in &extern_names { - debug!("loading extern crate {}", extern_name); - if let Err(()) = resolver - .resolve_str_path_error( - DUMMY_SP, - extern_name, - TypeNS, - LocalDefId { local_def_index: CRATE_DEF_INDEX }.to_def_id(), - ) { - warn!("unable to resolve external crate {} (do you have an unused `--extern` crate?)", extern_name) - } - } - }); - }); - - resolver -} - crate fn run_global_ctxt( tcx: TyCtxt<'_>, resolver: Rc<RefCell<interface::BoxedResolver>>, + resolver_caches: ResolverCaches, show_coverage: bool, render_options: RenderOptions, output_format: OutputFormat, @@ -355,6 +327,14 @@ crate fn run_global_ctxt( }); rustc_passes::stability::check_unused_or_stable_features(tcx); + let auto_traits = resolver_caches + .all_traits + .as_ref() + .expect("`all_traits` are already borrowed") + .iter() + .copied() + .filter(|&trait_def_id| tcx.trait_is_auto(trait_def_id)) + .collect(); let access_levels = AccessLevels { map: tcx.privacy_access_levels(()).map.iter().map(|(k, v)| (k.to_def_id(), *v)).collect(), }; @@ -362,16 +342,14 @@ crate fn run_global_ctxt( let mut ctxt = DocContext { tcx, resolver, + resolver_caches, param_env: ParamEnv::empty(), external_traits: Default::default(), active_extern_traits: Default::default(), substs: Default::default(), impl_trait_bounds: Default::default(), generated_synthetics: Default::default(), - auto_traits: tcx - .all_traits() - .filter(|&trait_def_id| tcx.trait_is_auto(trait_def_id)) - .collect(), + auto_traits, module_trait_cache: FxHashMap::default(), cache: Cache::new(access_levels, render_options.document_private), inlined: FxHashSet::default(), diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 6b9ccd37cfb..a8fef4a3178 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -4,13 +4,14 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX}; use rustc_middle::middle::privacy::AccessLevels; use rustc_middle::ty::TyCtxt; -use rustc_span::symbol::sym; +use rustc_span::{sym, Symbol}; use crate::clean::{self, types::ExternalLocation, ExternalCrate, ItemId, PrimitiveType}; use crate::core::DocContext; use crate::fold::DocFolder; use crate::formats::item_type::ItemType; use crate::formats::Impl; +use crate::html::format::join_with_double_colon; use crate::html::markdown::short_markdown_summary; use crate::html::render::search_index::get_function_type_for_search; use crate::html::render::IndexItem; @@ -39,11 +40,11 @@ crate struct Cache { /// URLs when a type is being linked to. External paths are not located in /// this map because the `External` type itself has all the information /// necessary. - crate paths: FxHashMap<DefId, (Vec<String>, ItemType)>, + crate paths: FxHashMap<DefId, (Vec<Symbol>, ItemType)>, /// Similar to `paths`, but only holds external paths. This is only used for /// generating explicit hyperlinks to other crates. - crate external_paths: FxHashMap<DefId, (Vec<String>, ItemType)>, + crate external_paths: FxHashMap<DefId, (Vec<Symbol>, ItemType)>, /// Maps local `DefId`s of exported types to fully qualified paths. /// Unlike 'paths', this mapping ignores any renames that occur @@ -55,7 +56,7 @@ crate struct Cache { /// to the path used if the corresponding type is inlined. By /// doing this, we can detect duplicate impls on a trait page, and only display /// the impl for the inlined type. - crate exact_paths: FxHashMap<DefId, Vec<String>>, + crate exact_paths: FxHashMap<DefId, Vec<Symbol>>, /// This map contains information about all known traits of this crate. /// Implementations of a crate should inherit the documentation of the @@ -92,7 +93,7 @@ crate struct Cache { crate masked_crates: FxHashSet<CrateNum>, // Private fields only used when initially crawling a crate to build a cache - stack: Vec<String>, + stack: Vec<Symbol>, parent_stack: Vec<DefId>, parent_is_trait_impl: bool, stripped_mod: bool, @@ -155,7 +156,7 @@ impl Cache { let dst = &render_options.output; let location = e.location(extern_url, extern_url_takes_precedence, dst, tcx); cx.cache.extern_locations.insert(e.crate_num, location); - cx.cache.external_paths.insert(e.def_id(), (vec![name.to_string()], ItemType::Module)); + cx.cache.external_paths.insert(e.def_id(), (vec![name], ItemType::Module)); } // FIXME: avoid this clone (requires implementing Default manually) @@ -164,10 +165,9 @@ impl Cache { let crate_name = tcx.crate_name(def_id.krate); // Recall that we only allow primitive modules to be at the root-level of the crate. // If that restriction is ever lifted, this will have to include the relative paths instead. - cx.cache.external_paths.insert( - def_id, - (vec![crate_name.to_string(), prim.as_sym().to_string()], ItemType::Primitive), - ); + cx.cache + .external_paths + .insert(def_id, (vec![crate_name, prim.as_sym()], ItemType::Primitive)); } krate = CacheBuilder { tcx, cache: &mut cx.cache }.fold_crate(krate); @@ -299,7 +299,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { self.cache.search_index.push(IndexItem { ty: item.type_(), name: s.to_string(), - path: path.join("::"), + path: join_with_double_colon(path), desc, parent, parent_idx: None, @@ -320,7 +320,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { // Keep track of the fully qualified path for this item. let pushed = match item.name { Some(n) if !n.is_empty() => { - self.cache.stack.push(n.to_string()); + self.cache.stack.push(n); true } _ => false, diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 981eb9589e9..8571a6a137f 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -19,6 +19,7 @@ use rustc_middle::ty; use rustc_middle::ty::DefIdTree; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::CRATE_DEF_INDEX; +use rustc_span::{sym, Symbol}; use rustc_target::spec::abi::Abi; use crate::clean::{ @@ -29,6 +30,7 @@ use crate::formats::item_type::ItemType; use crate::html::escape::Escape; use crate::html::render::Context; +use super::url_parts_builder::estimate_item_path_byte_length; use super::url_parts_builder::UrlPartsBuilder; crate trait Print { @@ -502,11 +504,22 @@ crate enum HrefError { NotInExternalCache, } +// Panics if `syms` is empty. +crate fn join_with_double_colon(syms: &[Symbol]) -> String { + let mut s = String::with_capacity(estimate_item_path_byte_length(syms.len())); + s.push_str(&syms[0].as_str()); + for sym in &syms[1..] { + s.push_str("::"); + s.push_str(&sym.as_str()); + } + s +} + crate fn href_with_root_path( did: DefId, cx: &Context<'_>, root_path: Option<&str>, -) -> Result<(String, ItemType, Vec<String>), HrefError> { +) -> Result<(String, ItemType, Vec<Symbol>), HrefError> { let tcx = cx.tcx(); let def_kind = tcx.def_kind(did); let did = match def_kind { @@ -518,7 +531,7 @@ crate fn href_with_root_path( }; let cache = cx.cache(); let relative_to = &cx.current; - fn to_module_fqp(shortty: ItemType, fqp: &[String]) -> &[String] { + fn to_module_fqp(shortty: ItemType, fqp: &[Symbol]) -> &[Symbol] { if shortty == ItemType::Module { fqp } else { &fqp[..fqp.len() - 1] } } @@ -533,9 +546,9 @@ crate fn href_with_root_path( let mut is_remote = false; let (fqp, shortty, mut url_parts) = match cache.paths.get(&did) { Some(&(ref fqp, shortty)) => (fqp, shortty, { - let module_fqp = to_module_fqp(shortty, fqp); + let module_fqp = to_module_fqp(shortty, fqp.as_slice()); debug!(?fqp, ?shortty, ?module_fqp); - href_relative_parts(module_fqp, relative_to) + href_relative_parts(module_fqp, relative_to).collect() }), None => { if let Some(&(ref fqp, shortty)) = cache.external_paths.get(&did) { @@ -548,10 +561,12 @@ crate fn href_with_root_path( is_remote = true; let s = s.trim_end_matches('/'); let mut builder = UrlPartsBuilder::singleton(s); - builder.extend(module_fqp.iter().map(String::as_str)); + builder.extend(module_fqp.iter().copied()); builder } - ExternalLocation::Local => href_relative_parts(module_fqp, relative_to), + ExternalLocation::Local => { + href_relative_parts(module_fqp, relative_to).collect() + } ExternalLocation::Unknown => return Err(HrefError::DocumentationNotBuilt), }, ) @@ -567,45 +582,50 @@ crate fn href_with_root_path( } } debug!(?url_parts); - let last = &fqp.last().unwrap()[..]; match shortty { ItemType::Module => { url_parts.push("index.html"); } _ => { - let filename = format!("{}.{}.html", shortty.as_str(), last); - url_parts.push(&filename); + let prefix = shortty.as_str(); + let last = fqp.last().unwrap(); + url_parts.push_fmt(format_args!("{}.{}.html", prefix, last)); } } Ok((url_parts.finish(), shortty, fqp.to_vec())) } -crate fn href(did: DefId, cx: &Context<'_>) -> Result<(String, ItemType, Vec<String>), HrefError> { +crate fn href(did: DefId, cx: &Context<'_>) -> Result<(String, ItemType, Vec<Symbol>), HrefError> { href_with_root_path(did, cx, None) } /// Both paths should only be modules. /// This is because modules get their own directories; that is, `std::vec` and `std::vec::Vec` will /// both need `../iter/trait.Iterator.html` to get at the iterator trait. -crate fn href_relative_parts(fqp: &[String], relative_to_fqp: &[String]) -> UrlPartsBuilder { +crate fn href_relative_parts<'fqp>( + fqp: &'fqp [Symbol], + relative_to_fqp: &[Symbol], +) -> Box<dyn Iterator<Item = Symbol> + 'fqp> { for (i, (f, r)) in fqp.iter().zip(relative_to_fqp.iter()).enumerate() { // e.g. linking to std::iter from std::vec (`dissimilar_part_count` will be 1) if f != r { let dissimilar_part_count = relative_to_fqp.len() - i; - let fqp_module = fqp[i..fqp.len()].iter().map(String::as_str); - return iter::repeat("..").take(dissimilar_part_count).chain(fqp_module).collect(); + let fqp_module = &fqp[i..fqp.len()]; + return box iter::repeat(sym::dotdot) + .take(dissimilar_part_count) + .chain(fqp_module.iter().copied()); } } // e.g. linking to std::sync::atomic from std::sync if relative_to_fqp.len() < fqp.len() { - fqp[relative_to_fqp.len()..fqp.len()].iter().map(String::as_str).collect() + box fqp[relative_to_fqp.len()..fqp.len()].iter().copied() // e.g. linking to std::sync from std::sync::atomic } else if fqp.len() < relative_to_fqp.len() { let dissimilar_part_count = relative_to_fqp.len() - fqp.len(); - iter::repeat("..").take(dissimilar_part_count).collect() + box iter::repeat(sym::dotdot).take(dissimilar_part_count) // linking to the same module } else { - UrlPartsBuilder::new() + box iter::empty() } } @@ -632,14 +652,14 @@ fn resolved_path<'cx>( if let Ok((_, _, fqp)) = href(did, cx) { format!( "{}::{}", - fqp[..fqp.len() - 1].join("::"), - anchor(did, fqp.last().unwrap(), cx) + join_with_double_colon(&fqp[..fqp.len() - 1]), + anchor(did, *fqp.last().unwrap(), cx) ) } else { last.name.to_string() } } else { - anchor(did, last.name.as_str(), cx).to_string() + anchor(did, last.name, cx).to_string() }; write!(w, "{}{}", path, last.args.print(cx))?; } @@ -668,30 +688,31 @@ fn primitive_link( needs_termination = true; } Some(&def_id) => { - let cname_sym; let loc = match m.extern_locations[&def_id.krate] { ExternalLocation::Remote(ref s) => { - cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx()); - Some(vec![s.trim_end_matches('/'), cname_sym.as_str()]) + let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx()); + let builder: UrlPartsBuilder = + [s.as_str().trim_end_matches('/'), cname_sym.as_str()] + .into_iter() + .collect(); + Some(builder) } ExternalLocation::Local => { - cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx()); - Some(if cx.current.first().map(|x| &x[..]) == Some(cname_sym.as_str()) { - iter::repeat("..").take(cx.current.len() - 1).collect() + let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx()); + Some(if cx.current.first() == Some(&cname_sym) { + iter::repeat(sym::dotdot).take(cx.current.len() - 1).collect() } else { - let cname = iter::once(cname_sym.as_str()); - iter::repeat("..").take(cx.current.len()).chain(cname).collect() + iter::repeat(sym::dotdot) + .take(cx.current.len()) + .chain(iter::once(cname_sym)) + .collect() }) } ExternalLocation::Unknown => None, }; - if let Some(loc) = loc { - write!( - f, - "<a class=\"primitive\" href=\"{}/primitive.{}.html\">", - loc.join("/"), - prim.as_sym() - )?; + if let Some(mut loc) = loc { + loc.push_fmt(format_args!("primitive.{}.html", prim.as_sym())); + write!(f, "<a class=\"primitive\" href=\"{}\">", loc.finish())?; needs_termination = true; } } @@ -730,7 +751,7 @@ fn tybounds<'a, 'tcx: 'a>( crate fn anchor<'a, 'cx: 'a>( did: DefId, - text: &'a str, + text: Symbol, cx: &'cx Context<'_>, ) -> impl fmt::Display + 'a { let parts = href(did, cx); @@ -742,8 +763,8 @@ crate fn anchor<'a, 'cx: 'a>( short_ty, url, short_ty, - fqp.join("::"), - text + join_with_double_colon(&fqp), + text.as_str() ) } else { write!(f, "{}", text) @@ -960,7 +981,7 @@ fn fmt_type<'cx>( url = url, shortty = ItemType::AssocType, name = name, - path = path.join("::") + path = join_with_double_colon(path), )?; } _ => write!(f, "{}", name)?, @@ -1270,7 +1291,7 @@ impl clean::Visibility { debug!("path={:?}", path); // modified from `resolved_path()` to work with `DefPathData` let last_name = path.data.last().unwrap().data.get_opt_name().unwrap(); - let anchor = anchor(vis_did, last_name.as_str(), cx).to_string(); + let anchor = anchor(vis_did, last_name, cx).to_string(); let mut s = "pub(in ".to_owned(); for seg in &path.data[..path.data.len() - 1] { diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs index 3d3fa3aaeaa..4ca71ea8684 100644 --- a/src/librustdoc/html/layout.rs +++ b/src/librustdoc/html/layout.rs @@ -7,9 +7,9 @@ use crate::externalfiles::ExternalHtml; use crate::html::format::{Buffer, Print}; use crate::html::render::{ensure_trailing_slash, StylePath}; -use serde::Serialize; +use askama::Template; -#[derive(Clone, Serialize)] +#[derive(Clone)] crate struct Layout { crate logo: String, crate favicon: String, @@ -19,14 +19,10 @@ crate struct Layout { /// The given user css file which allow to customize the generated /// documentation theme. crate css_file_extension: Option<PathBuf>, - /// If false, the `select` element to have search filtering by crates on rendered docs - /// won't be generated. - crate generate_search_filter: bool, /// If true, then scrape-examples.js will be included in the output HTML file crate scrape_examples_extension: bool, } -#[derive(Serialize)] crate struct Page<'a> { crate title: &'a str, crate css_class: &'a str, @@ -45,7 +41,8 @@ impl<'a> Page<'a> { } } -#[derive(Serialize)] +#[derive(Template)] +#[template(path = "page.html")] struct PageLayout<'a> { static_root_path: &'a str, page: &'a Page<'a>, @@ -58,7 +55,6 @@ struct PageLayout<'a> { } crate fn render<T: Print, S: Print>( - templates: &tera::Tera, layout: &Layout, page: &Page<'_>, sidebar: S, @@ -76,7 +72,7 @@ crate fn render<T: Print, S: Print>( let rustdoc_version = rustc_interface::util::version_str().unwrap_or("unknown version"); let content = Buffer::html().to_display(t); // Note: This must happen before making the sidebar. let sidebar = Buffer::html().to_display(sidebar); - let teractx = tera::Context::from_serialize(PageLayout { + PageLayout { static_root_path, page, layout, @@ -85,9 +81,9 @@ crate fn render<T: Print, S: Print>( content, krate_with_trailing_slash, rustdoc_version, - }) - .unwrap(); - templates.render("page.html", &teractx).unwrap() + } + .render() + .unwrap() } crate fn redirect(url: &str) -> String { diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 534a542d58e..865e14f694d 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -11,11 +11,10 @@ use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::edition::Edition; use rustc_span::source_map::FileName; -use rustc_span::symbol::sym; +use rustc_span::{sym, Symbol}; use super::print_item::{full_path, item_path, print_item}; use super::search_index::build_index; -use super::templates; use super::write_shared::write_shared; use super::{ collect_spans_and_sources, print_sidebar, settings, AllTypes, LinkFromSrc, NameDoc, StylePath, @@ -30,7 +29,7 @@ use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; use crate::formats::FormatRenderer; use crate::html::escape::Escape; -use crate::html::format::Buffer; +use crate::html::format::{join_with_double_colon, Buffer}; use crate::html::markdown::{self, plain_text_summary, ErrorCodes, IdMap}; use crate::html::{layout, sources}; use crate::scrape_examples::AllCallLocations; @@ -46,7 +45,7 @@ use crate::try_err; crate struct Context<'tcx> { /// Current hierarchy of components leading down to what's currently being /// rendered - pub(crate) current: Vec<String>, + pub(crate) current: Vec<Symbol>, /// The current destination folder of where HTML artifacts should be placed. /// This changes as the context descends into the module hierarchy. crate dst: PathBuf, @@ -65,7 +64,7 @@ crate struct Context<'tcx> { /// /// [#82381]: https://github.com/rust-lang/rust/issues/82381 crate shared: Rc<SharedContext<'tcx>>, - /// This flag indicates whether `[src]` links should be generated or not. If + /// This flag indicates whether source links should be generated or not. If /// the source files are present in the html rendering, then this will be /// `true`. crate include_sources: bool, @@ -118,8 +117,6 @@ crate struct SharedContext<'tcx> { /// the crate. redirections: Option<RefCell<FxHashMap<String, String>>>, - pub(crate) templates: tera::Tera, - /// Correspondance map used to link types used in the source code pages to allow to click on /// links to jump to the type's definition. crate span_correspondance_map: FxHashMap<rustc_span::Span, LinkFromSrc>, @@ -179,7 +176,7 @@ impl<'tcx> Context<'tcx> { title.push_str(" in "); } // No need to include the namespace for primitive types and keywords - title.push_str(&self.current.join("::")); + title.push_str(&join_with_double_colon(&self.current)); }; title.push_str(" - Rust"); let tyname = it.type_(); @@ -218,29 +215,28 @@ impl<'tcx> Context<'tcx> { if !self.render_redirect_pages { layout::render( - &self.shared.templates, &self.shared.layout, &page, |buf: &mut _| print_sidebar(self, it, buf), - |buf: &mut _| print_item(self, &self.shared.templates, it, buf, &page), + |buf: &mut _| print_item(self, it, buf, &page), &self.shared.style_files, ) } else { if let Some(&(ref names, ty)) = self.cache().paths.get(&it.def_id.expect_def_id()) { let mut path = String::new(); for name in &names[..names.len() - 1] { - path.push_str(name); + path.push_str(&name.as_str()); path.push('/'); } - path.push_str(&item_path(ty, names.last().unwrap())); + path.push_str(&item_path(ty, &names.last().unwrap().as_str())); match self.shared.redirections { Some(ref redirections) => { let mut current_path = String::new(); for name in &self.current { - current_path.push_str(name); + current_path.push_str(&name.as_str()); current_path.push('/'); } - current_path.push_str(&item_path(ty, names.last().unwrap())); + current_path.push_str(&item_path(ty, &names.last().unwrap().as_str())); redirections.borrow_mut().insert(current_path, path); } None => return layout::redirect(&format!("{}{}", self.root_path(), path)), @@ -391,7 +387,6 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { extension_css, resource_suffix, static_root_path, - generate_search_filter, unstable_features, generate_redirect_map, show_type_layout, @@ -421,12 +416,10 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { default_settings, krate: krate.name(tcx).to_string(), css_file_extension: extension_css, - generate_search_filter, scrape_examples_extension: !call_locations.is_empty(), }; let mut issue_tracker_base_url = None; let mut include_sources = true; - let templates = templates::load()?; // Crawl the crate attributes looking for attributes which control how we're // going to emit HTML @@ -481,7 +474,6 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { errors: receiver, redirections: if generate_redirect_map { Some(Default::default()) } else { None }, show_type_layout, - templates, span_correspondance_map: matches, cache, call_locations, @@ -577,7 +569,6 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { }; let all = self.shared.all.replace(AllTypes::new()); let v = layout::render( - &self.shared.templates, &self.shared.layout, &page, sidebar, @@ -599,7 +590,6 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { .map(StylePath::basename) .collect::<Result<_, Error>>()?; let v = layout::render( - &self.shared.templates, &self.shared.layout, &page, sidebar, @@ -644,8 +634,8 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { self.render_redirect_pages = item.is_stripped(); } let scx = &self.shared; - let item_name = item.name.as_ref().unwrap().to_string(); - self.dst.push(&item_name); + let item_name = item.name.unwrap(); + self.dst.push(&*item_name.as_str()); self.current.push(item_name); info!("Recursing into {}", self.dst.display()); @@ -665,7 +655,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { _ => unreachable!(), }; let items = self.build_sidebar_items(module); - let js_dst = self.dst.join("sidebar-items.js"); + let js_dst = self.dst.join(&format!("sidebar-items{}.js", self.shared.resource_suffix)); let v = format!("initSidebarItems({});", serde_json::to_string(&items).unwrap()); scx.fs.write(js_dst, v)?; } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 16334890da6..f0cbe79bd06 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -31,7 +31,6 @@ mod tests; mod context; mod print_item; mod span_map; -mod templates; mod write_shared; crate use self::context::*; @@ -70,8 +69,9 @@ use crate::formats::item_type::ItemType; use crate::formats::{AssocItemRender, Impl, RenderMode}; use crate::html::escape::Escape; use crate::html::format::{ - href, print_abi_with_space, print_constness_with_space, print_default_space, - print_generic_bounds, print_where_clause, Buffer, HrefError, PrintWithSpace, + href, join_with_double_colon, print_abi_with_space, print_constness_with_space, + print_default_space, print_generic_bounds, print_where_clause, Buffer, HrefError, + PrintWithSpace, }; use crate::html::highlight; use crate::html::markdown::{HeadingOffset, Markdown, MarkdownHtml, MarkdownSummaryLine}; @@ -182,7 +182,7 @@ impl StylePath { fn write_srclink(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) { if let Some(l) = cx.src_href(item) { - write!(buf, "<a class=\"srclink\" href=\"{}\" title=\"goto source code\">[src]</a>", l) + write!(buf, "<a class=\"srclink\" href=\"{}\" title=\"goto source code\">source</a>", l) } } @@ -312,14 +312,6 @@ impl AllTypes { f.write_str( "<h1 class=\"fqn\">\ <span class=\"in-band\">List of all items</span>\ - <span class=\"out-of-band\">\ - <span id=\"render-detail\">\ - <a id=\"toggle-all-docs\" href=\"javascript:void(0)\" \ - title=\"collapse all docs\">\ - [<span class=\"inner\">−</span>]\ - </a>\ - </span> - </span> </h1>", ); // Note: print_entries does not escape the title, because we know the current set of titles @@ -807,7 +799,7 @@ fn render_stability_since_raw( const_stability: Option<ConstStability>, containing_ver: Option<Symbol>, containing_const_ver: Option<Symbol>, -) { +) -> bool { let ver = ver.filter(|inner| !inner.is_empty()); match (ver, const_stability) { @@ -850,8 +842,9 @@ fn render_stability_since_raw( v ); } - _ => {} + _ => return false, } + true } fn render_assoc_item( @@ -1198,11 +1191,9 @@ fn render_deref_methods( } } render_assoc_items_inner(w, cx, container_item, did, what, derefs); - } else { - if let Some(prim) = target.primitive_type() { - if let Some(&did) = cache.primitive_locations.get(&prim) { - render_assoc_items_inner(w, cx, container_item, did, what, derefs); - } + } else if let Some(prim) = target.primitive_type() { + if let Some(&did) = cache.primitive_locations.get(&prim) { + render_assoc_items_inner(w, cx, container_item, did, what, derefs); } } } @@ -1640,7 +1631,7 @@ fn render_impl( } // Render the items that appear on the right side of methods, impls, and -// associated types. For example "1.0.0 (const: 1.39.0) [src]". +// associated types. For example "1.0.0 (const: 1.39.0) · source". fn render_rightside( w: &mut Buffer, cx: &Context<'_>, @@ -1658,13 +1649,16 @@ fn render_rightside( }; write!(w, "<div class=\"rightside\">"); - render_stability_since_raw( + let has_stability = render_stability_since_raw( w, item.stable_since(tcx), const_stability, containing_item.stable_since(tcx), const_stable_since, ); + if has_stability { + w.write_str(" · "); + } write_srclink(cx, item, w); w.write_str("</div>"); @@ -1831,7 +1825,11 @@ fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) { ty = it.type_(), path = relpath ); - write!(buffer, "<script defer src=\"{}sidebar-items.js\"></script>", relpath); + write!( + buffer, + "<script defer src=\"{}sidebar-items{}.js\"></script>", + relpath, cx.shared.resource_suffix + ); // Closes sidebar-elems div. buffer.write_str("</div>"); } @@ -2516,7 +2514,7 @@ fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec<String> { let fqp = cache.exact_paths.get(&did).cloned().or_else(get_extern); if let Some(path) = fqp { - out.push(path.join("::")); + out.push(join_with_double_colon(&path)); } }; diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 44a9ec5ea42..67821f19a23 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -26,13 +26,15 @@ use crate::formats::item_type::ItemType; use crate::formats::{AssocItemRender, Impl, RenderMode}; use crate::html::escape::Escape; use crate::html::format::{ - print_abi_with_space, print_constness_with_space, print_where_clause, Buffer, PrintWithSpace, + join_with_double_colon, print_abi_with_space, print_constness_with_space, print_where_clause, + Buffer, PrintWithSpace, }; use crate::html::highlight; use crate::html::layout::Page; use crate::html::markdown::{HeadingOffset, MarkdownSummaryLine}; +use crate::html::url_parts_builder::UrlPartsBuilder; -use serde::Serialize; +use askama::Template; const ITEM_TABLE_OPEN: &str = "<div class=\"item-table\">"; const ITEM_TABLE_CLOSE: &str = "</div>"; @@ -40,31 +42,25 @@ const ITEM_TABLE_ROW_OPEN: &str = "<div class=\"item-row\">"; const ITEM_TABLE_ROW_CLOSE: &str = "</div>"; // A component in a `use` path, like `string` in std::string::ToString -#[derive(Serialize)] -struct PathComponent<'a> { +struct PathComponent { path: String, - name: &'a str, + name: Symbol, } -#[derive(Serialize)] +#[derive(Template)] +#[template(path = "print_item.html")] struct ItemVars<'a> { page: &'a Page<'a>, static_root_path: &'a str, typ: &'a str, name: &'a str, item_type: &'a str, - path_components: Vec<PathComponent<'a>>, + path_components: Vec<PathComponent>, stability_since_raw: &'a str, src_href: Option<&'a str>, } -pub(super) fn print_item( - cx: &Context<'_>, - templates: &tera::Tera, - item: &clean::Item, - buf: &mut Buffer, - page: &Page<'_>, -) { +pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer, page: &Page<'_>) { debug_assert!(!item.is_stripped()); let typ = match *item.kind { clean::ModuleItem(_) => { @@ -108,10 +104,10 @@ pub(super) fn print_item( ); let stability_since_raw: String = stability_since_raw.into_inner(); - // Write `src` tag + // Write source tag // // When this item is part of a `crate use` in a downstream crate, the - // [src] link in the downstream documentation will actually come back to + // source link in the downstream documentation will actually come back to // this page, and this link will be auto-clicked. The `id` attribute is // used to find the link to auto-click. let src_href = @@ -127,24 +123,23 @@ pub(super) fn print_item( .take(amt) .map(|(i, component)| PathComponent { path: "../".repeat(cur.len() - i - 1), - name: component, + name: *component, }) .collect() }; let item_vars = ItemVars { - page: page, + page, static_root_path: page.get_static_root_path(), - typ: typ, + typ, name: item.name.as_ref().unwrap().as_str(), item_type: &item.type_().to_string(), - path_components: path_components, + path_components, stability_since_raw: &stability_since_raw, src_href: src_href.as_deref(), }; - let teractx = tera::Context::from_serialize(item_vars).unwrap(); - let heading = templates.render("print_item.html", &teractx).unwrap(); + let heading = item_vars.render().unwrap(); buf.write_str(&heading); match *item.kind { @@ -269,7 +264,7 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl indices.dedup_by_key(|i| { ( items[*i].def_id, - if items[*i].name.as_ref().is_some() { Some(full_path(cx, &items[*i])) } else { None }, + if items[*i].name.is_some() { Some(full_path(cx, &items[*i])) } else { None }, items[*i].type_(), if items[*i].is_import() { *i } else { 0 }, ) @@ -311,22 +306,18 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl w.write_str(ITEM_TABLE_ROW_OPEN); match *src { - Some(ref src) => write!( + Some(src) => write!( w, "<div class=\"item-left\"><code>{}extern crate {} as {};", myitem.visibility.print_with_space(myitem.def_id, cx), - anchor(myitem.def_id.expect_def_id(), src.as_str(), cx), - myitem.name.as_ref().unwrap(), + anchor(myitem.def_id.expect_def_id(), src, cx), + myitem.name.unwrap(), ), None => write!( w, "<div class=\"item-left\"><code>{}extern crate {};", myitem.visibility.print_with_space(myitem.def_id, cx), - anchor( - myitem.def_id.expect_def_id(), - myitem.name.as_ref().unwrap().as_str(), - cx - ), + anchor(myitem.def_id.expect_def_id(), myitem.name.unwrap(), cx), ), } w.write_str("</code></div>"); @@ -398,7 +389,7 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl {stab_tags}\ </div>\ <div class=\"item-right docblock-short\">{docs}</div>", - name = *myitem.name.as_ref().unwrap(), + name = myitem.name.unwrap(), stab_tags = extra_info_tags(myitem, item, cx.tcx()), docs = MarkdownSummaryLine(&doc_value, &myitem.links(cx)).into_string(), class = myitem.type_(), @@ -470,7 +461,7 @@ fn item_function(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, f: &clean:: let asyncness = f.header.asyncness.print_with_space(); let unsafety = f.header.unsafety.print_with_space(); let abi = print_abi_with_space(f.header.abi).to_string(); - let name = it.name.as_ref().unwrap(); + let name = it.name.unwrap(); let generics_len = format!("{:#}", f.generics.print(cx)).len(); let header_len = "fn ".len() @@ -526,7 +517,7 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra it.visibility.print_with_space(it.def_id, cx), t.unsafety.print_with_space(), if t.is_auto { "auto " } else { "" }, - it.name.as_ref().unwrap(), + it.name.unwrap(), t.generics.print(cx), bounds ); @@ -667,7 +658,7 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra } fn trait_item(w: &mut Buffer, cx: &Context<'_>, m: &clean::Item, t: &clean::Item) { - let name = m.name.as_ref().unwrap(); + let name = m.name.unwrap(); info!("Documenting {} on {:?}", name, t.name); let item_type = m.type_(); let id = cx.derive_id(format!("{}.{}", item_type, name)); @@ -864,20 +855,21 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra } } + let mut js_src_path: UrlPartsBuilder = std::iter::repeat("..") + .take(cx.current.len()) + .chain(std::iter::once("implementors")) + .collect(); + if it.def_id.is_local() { + js_src_path.extend(cx.current.iter().copied()); + } else { + let (ref path, _) = cache.external_paths[&it.def_id.expect_def_id()]; + js_src_path.extend(path[..path.len() - 1].iter().copied()); + } + js_src_path.push_fmt(format_args!("{}.{}.js", it.type_(), it.name.unwrap())); write!( w, - "<script type=\"text/javascript\" \ - src=\"{root_path}/implementors/{path}/{ty}.{name}.js\" async>\ - </script>", - root_path = vec![".."; cx.current.len()].join("/"), - path = if it.def_id.is_local() { - cx.current.join("/") - } else { - let (ref path, _) = cache.external_paths[&it.def_id.expect_def_id()]; - path[..path.len() - 1].join("/") - }, - ty = it.type_(), - name = *it.name.as_ref().unwrap() + "<script type=\"text/javascript\" src=\"{src}\" async></script>", + src = js_src_path.finish(), ); } @@ -888,7 +880,7 @@ fn item_trait_alias(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clea write!( w, "trait {}{}{} = {};", - it.name.as_ref().unwrap(), + it.name.unwrap(), t.generics.print(cx), print_where_clause(&t.generics, cx, 0, true), bounds(&t.bounds, true, cx) @@ -912,7 +904,7 @@ fn item_opaque_ty(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean: write!( w, "type {}{}{where_clause} = impl {bounds};", - it.name.as_ref().unwrap(), + it.name.unwrap(), t.generics.print(cx), where_clause = print_where_clause(&t.generics, cx, 0, true), bounds = bounds(&t.bounds, false, cx), @@ -951,7 +943,7 @@ fn item_typedef( write!( w, "type {}{}{where_clause} = {type_};", - it.name.as_ref().unwrap(), + it.name.unwrap(), t.generics.print(cx), where_clause = print_where_clause(&t.generics, cx, 0, true), type_ = t.type_.print(cx), @@ -1004,7 +996,7 @@ fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Uni Fields<a href=\"#fields\" class=\"anchor\"></a></h2>" ); for (field, ty) in fields { - let name = field.name.as_ref().expect("union field name"); + let name = field.name.expect("union field name"); let id = format!("{}.{}", ItemType::StructField, name); write!( w, @@ -1049,7 +1041,7 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum w, "{}enum {}{}{}", it.visibility.print_with_space(it.def_id, cx), - it.name.as_ref().unwrap(), + it.name.unwrap(), e.generics.print(cx), print_where_clause(&e.generics, cx, 0, true), ); @@ -1064,7 +1056,7 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum } for v in &e.variants { w.write_str(" "); - let name = v.name.as_ref().unwrap(); + let name = v.name.unwrap(); match *v.kind { clean::VariantItem(ref var) => match var { clean::Variant::CLike => write!(w, "{}", name), @@ -1113,15 +1105,14 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum ); document_non_exhaustive(w, it); for variant in &e.variants { - let id = - cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.as_ref().unwrap())); + let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap())); write!( w, "<h3 id=\"{id}\" class=\"variant small-section-header\">\ <a href=\"#{id}\" class=\"anchor field\"></a>\ <code>{name}", id = id, - name = variant.name.as_ref().unwrap() + name = variant.name.unwrap() ); if let clean::VariantItem(clean::Variant::Tuple(ref s)) = *variant.kind { w.write_str("("); @@ -1133,18 +1124,24 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum w.write_str("</h3>"); use crate::clean::Variant; - if let Some((extra, fields)) = match *variant.kind { - clean::VariantItem(Variant::Struct(ref s)) => Some(("", &s.fields)), - clean::VariantItem(Variant::Tuple(ref fields)) => Some(("Tuple ", fields)), + + let heading_and_fields = match &*variant.kind { + clean::VariantItem(Variant::Struct(s)) => Some(("Fields", &s.fields)), + // Documentation on tuple variant fields is rare, so to reduce noise we only emit + // the section if at least one field is documented. + clean::VariantItem(Variant::Tuple(fields)) + if fields.iter().any(|f| f.doc_value().is_some()) => + { + Some(("Tuple Fields", fields)) + } _ => None, - } { - let variant_id = cx.derive_id(format!( - "{}.{}.fields", - ItemType::Variant, - variant.name.as_ref().unwrap() - )); + }; + + if let Some((heading, fields)) = heading_and_fields { + let variant_id = + cx.derive_id(format!("{}.{}.fields", ItemType::Variant, variant.name.unwrap())); write!(w, "<div class=\"sub-variant\" id=\"{id}\">", id = variant_id); - write!(w, "<h4>{extra}Fields</h4>", extra = extra,); + write!(w, "<h4>{heading}</h4>", heading = heading); document_non_exhaustive(w, variant); for field in fields { match *field.kind { @@ -1152,8 +1149,8 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum clean::StructFieldItem(ref ty) => { let id = cx.derive_id(format!( "variant.{}.field.{}", - variant.name.as_ref().unwrap(), - field.name.as_ref().unwrap() + variant.name.unwrap(), + field.name.unwrap() )); write!( w, @@ -1163,7 +1160,7 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum <code>{f}: {t}</code>\ </span>", id = id, - f = field.name.as_ref().unwrap(), + f = field.name.unwrap(), t = ty.print(cx) ); document(w, cx, field, Some(variant), HeadingOffset::H5); @@ -1202,7 +1199,7 @@ fn item_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Mac fn item_proc_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, m: &clean::ProcMacro) { wrap_into_docblock(w, |w| { - let name = it.name.as_ref().expect("proc-macros always have names"); + let name = it.name.expect("proc-macros always have names"); match m.kind { MacroKind::Bang => { wrap_item(w, "macro", |w| { @@ -1246,7 +1243,7 @@ fn item_constant(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, c: &clean:: w, "{vis}const {name}: {typ}", vis = it.visibility.print_with_space(it.def_id, cx), - name = it.name.as_ref().unwrap(), + name = it.name.unwrap(), typ = c.type_.print(cx), ); @@ -1339,7 +1336,7 @@ fn item_static(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::St "{vis}static {mutability}{name}: {typ}", vis = it.visibility.print_with_space(it.def_id, cx), mutability = s.mutability.print_with_space(), - name = it.name.as_ref().unwrap(), + name = it.name.unwrap(), typ = s.type_.print(cx) ); }); @@ -1356,7 +1353,7 @@ fn item_foreign_type(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) { w, " {}type {};\n}}", it.visibility.print_with_space(it.def_id, cx), - it.name.as_ref().unwrap(), + it.name.unwrap(), ); }); }); @@ -1408,7 +1405,7 @@ crate fn compare_names(mut lhs: &str, mut rhs: &str) -> Ordering { } pub(super) fn full_path(cx: &Context<'_>, item: &clean::Item) -> String { - let mut s = cx.current.join("::"); + let mut s = join_with_double_colon(&cx.current); s.push_str("::"); s.push_str(item.name.unwrap().as_str()); s @@ -1467,7 +1464,7 @@ fn render_stability_since( item.const_stability(tcx), containing_item.stable_since(tcx), containing_item.const_stable_since(tcx), - ) + ); } fn compare_impl<'a, 'b>(lhs: &'a &&Impl, rhs: &'b &&Impl, cx: &Context<'_>) -> Ordering { @@ -1524,12 +1521,7 @@ fn render_union( tab: &str, cx: &Context<'_>, ) { - write!( - w, - "{}union {}", - it.visibility.print_with_space(it.def_id, cx), - it.name.as_ref().unwrap() - ); + write!(w, "{}union {}", it.visibility.print_with_space(it.def_id, cx), it.name.unwrap()); if let Some(g) = g { write!(w, "{}", g.print(cx)); write!(w, "{}", print_where_clause(g, cx, 0, true)); @@ -1549,7 +1541,7 @@ fn render_union( w, " {}{}: {},\n{}", field.visibility.print_with_space(field.def_id, cx), - field.name.as_ref().unwrap(), + field.name.unwrap(), ty.print(cx), tab ); @@ -1557,7 +1549,7 @@ fn render_union( } if it.has_stripped_fields().unwrap() { - write!(w, " // some fields omitted\n{}", tab); + write!(w, " /* private fields */\n{}", tab); } if toggle { toggle_close(w); @@ -1580,7 +1572,7 @@ fn render_struct( "{}{}{}", it.visibility.print_with_space(it.def_id, cx), if structhead { "struct " } else { "" }, - it.name.as_ref().unwrap() + it.name.unwrap() ); if let Some(g) = g { write!(w, "{}", g.print(cx)) @@ -1605,7 +1597,7 @@ fn render_struct( "\n{} {}{}: {},", tab, field.visibility.print_with_space(field.def_id, cx), - field.name.as_ref().unwrap(), + field.name.unwrap(), ty.print(cx), ); } @@ -1613,13 +1605,11 @@ fn render_struct( if has_visible_fields { if it.has_stripped_fields().unwrap() { - write!(w, "\n{} // some fields omitted", tab); + write!(w, "\n{} /* private fields */", tab); } write!(w, "\n{}", tab); } else if it.has_stripped_fields().unwrap() { - // If there are no visible fields we can just display - // `{ /* fields omitted */ }` to save space. - write!(w, " /* fields omitted */ "); + write!(w, " /* private fields */ "); } if toggle { toggle_close(w); @@ -1776,8 +1766,8 @@ fn document_type_layout(w: &mut Buffer, cx: &Context<'_>, ty_def_id: DefId) { }; for (index, layout) in variants.iter_enumerated() { - let ident = adt.variants[index].ident; - write!(w, "<li><code>{name}</code>: ", name = ident); + let name = adt.variants[index].name; + write!(w, "<li><code>{name}</code>: ", name = name); write_size_of_layout(w, layout, tag_size); writeln!(w, "</li>"); } diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 0fbe090f219..87138b9571c 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -10,6 +10,7 @@ use crate::clean; use crate::clean::types::{FnRetTy, Function, GenericBound, Generics, Type, WherePredicate}; use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; +use crate::html::format::join_with_double_colon; use crate::html::markdown::short_markdown_summary; use crate::html::render::{IndexItem, IndexItemFunctionType, RenderType, TypeWithKind}; @@ -28,7 +29,7 @@ crate fn build_index<'tcx>(krate: &clean::Crate, cache: &mut Cache, tcx: TyCtxt< cache.search_index.push(IndexItem { ty: item.type_(), name: item.name.unwrap().to_string(), - path: fqp[..fqp.len() - 1].join("::"), + path: join_with_double_colon(&fqp[..fqp.len() - 1]), desc, parent: Some(did), parent_idx: None, @@ -102,7 +103,7 @@ crate fn build_index<'tcx>(krate: &clean::Crate, cache: &mut Cache, tcx: TyCtxt< struct CrateData<'a> { doc: String, items: Vec<&'a IndexItem>, - paths: Vec<(ItemType, String)>, + paths: Vec<(ItemType, Symbol)>, // The String is alias name and the vec is the list of the elements with this alias. // // To be noted: the `usize` elements are indexes to `items`. @@ -154,7 +155,10 @@ crate fn build_index<'tcx>(krate: &clean::Crate, cache: &mut Cache, tcx: TyCtxt< "f", &self.items.iter().map(|item| &item.search_type).collect::<Vec<_>>(), )?; - crate_data.serialize_field("p", &self.paths)?; + crate_data.serialize_field( + "p", + &self.paths.iter().map(|(it, s)| (it, s.to_string())).collect::<Vec<_>>(), + )?; if has_aliases { crate_data.serialize_field("a", &self.aliases)?; } diff --git a/src/librustdoc/html/render/templates.rs b/src/librustdoc/html/render/templates.rs deleted file mode 100644 index d1f18239447..00000000000 --- a/src/librustdoc/html/render/templates.rs +++ /dev/null @@ -1,20 +0,0 @@ -use std::error::Error as StdError; - -use crate::error::Error; - -pub(crate) fn load() -> Result<tera::Tera, Error> { - let mut templates = tera::Tera::default(); - - macro_rules! include_template { - ($file:literal, $fullpath:literal) => { - templates.add_raw_template($file, include_str!($fullpath)).map_err(|e| Error { - file: $file.into(), - error: format!("{}: {}", e, e.source().map(|e| e.to_string()).unwrap_or_default()), - })? - }; - } - - include_template!("page.html", "../templates/page.html"); - include_template!("print_item.html", "../templates/print_item.html"); - Ok(templates) -} diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index 563f4ae7385..3fa16c6c3b8 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -240,7 +240,7 @@ pub(super) fn write_shared( } if (*cx.shared).layout.logo.is_empty() { - write_toolchain("rust-logo.png", static_files::RUST_LOGO)?; + write_toolchain("rust-logo.svg", static_files::RUST_LOGO_SVG)?; } if (*cx.shared).layout.favicon.is_empty() { write_toolchain("favicon.svg", static_files::RUST_FAVICON_SVG)?; @@ -494,14 +494,7 @@ pub(super) fn write_shared( }) .collect::<String>() ); - let v = layout::render( - &cx.shared.templates, - &cx.shared.layout, - &page, - "", - content, - &cx.shared.style_files, - ); + let v = layout::render(&cx.shared.layout, &page, "", content, &cx.shared.style_files); cx.shared.fs.write(dst, v)?; } } @@ -569,7 +562,7 @@ pub(super) fn write_shared( let mut mydst = dst.clone(); for part in &remote_path[..remote_path.len() - 1] { - mydst.push(part); + mydst.push(part.to_string()); } cx.shared.ensure_dir(&mydst)?; mydst.push(&format!("{}.{}.js", remote_item_type, remote_path[remote_path.len() - 1])); diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index 04c2b7a0c9a..bae04f2095a 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -203,7 +203,6 @@ impl SourceCollector<'_, '_> { static_extra_scripts: &[&format!("source-script{}", self.cx.shared.resource_suffix)], }; let v = layout::render( - &self.cx.shared.templates, &self.cx.shared.layout, &page, "", diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 28cb8ae48e2..d7f33d6131c 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -137,17 +137,25 @@ h1, h2, h3, h4 { margin: 15px 0 5px 0; } h1.fqn { + margin: 0; + padding: 0; +} +.main-heading { display: flex; - border-bottom: 1px dashed; - margin-top: 0; + border-bottom: 1px dashed #DDDDDD; + padding-bottom: 6px; + margin-bottom: 15px; /* workaround to keep flex from breaking below 700 px width due to the float: right on the nav above the h1 */ padding-left: 1px; } -h1.fqn > .in-band > a:hover { +.main-heading a:hover { text-decoration: underline; } +#toggle-all-docs { + text-decoration: none; +} /* The only headings that get underlines are: Markdown-generated headings within the top-doc Rustdoc-generated h2 section headings (e.g. "Implementations", "Required Methods", etc) @@ -195,11 +203,13 @@ h1, h2, h3, h4, h5, h6, .sidebar, a.source, .search-input, .search-results .result-name, .content table td:first-child > a, .item-left > a, -div.item-list .out-of-band, span.since, +.out-of-band, +span.since, #source-sidebar, #sidebar-toggle, details.rustdoc-toggle > summary::before, div.impl-items > div:not(.docblock):not(.item-info), -.content ul.crate a.crate, a.srclink, +.content ul.crate a.crate, +a.srclink, /* This selector is for the items listed in the "all items" page. */ #main-content > ul.docblock > li > a { font-family: "Fira Sans", Arial, NanumBarunGothic, sans-serif; @@ -545,6 +555,10 @@ nav.sub { position: relative; } +.search-loading { + text-align: center; +} + #results > table { width: 100%; table-layout: fixed; @@ -605,10 +619,12 @@ nav.sub { .content .out-of-band { flex-grow: 0; text-align: right; - font-size: 1.4375rem; - margin: 0px; + margin-left: auto; + margin-right: 0; + font-size: 1.15rem; padding: 0 0 0 12px; font-weight: normal; + float: right; } .method > .code-header, .trait-impl > .code-header, .invisible > .code-header { @@ -862,18 +878,24 @@ h2.small-section-header > .anchor { display: inline-flex; width: calc(100% - 63px); } +.search-results-title { + display: inline; +} +#search-settings { + font-size: 1.5rem; + font-weight: 500; + margin-bottom: 20px; +} #crate-search { min-width: 115px; margin-top: 5px; - padding: 6px; - padding-right: 19px; - flex: none; + margin-left: 0.2em; + padding-left: 0.3em; + padding-right: 23px; border: 0; - border-right: 0; - border-radius: 4px 0 0 4px; + border-radius: 4px; outline: none; cursor: pointer; - border-right: 1px solid; -moz-appearance: none; -webkit-appearance: none; /* Removes default arrow from firefox */ @@ -1072,7 +1094,7 @@ body.blur > :not(#help) { font-size: initial; } -.impl-items .since, .impl .since, .methods .since { +.rightside { padding-left: 12px; padding-right: 2px; position: initial; @@ -1150,10 +1172,6 @@ a.test-arrow:hover{ font-weight: 300; } -.since + .srclink { - padding-left: 10px; -} - .item-spacer { width: 100%; height: 12px; diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css index 6ed7845e83a..23ee87a4e68 100644 --- a/src/librustdoc/html/static/css/themes/ayu.css +++ b/src/librustdoc/html/static/css/themes/ayu.css @@ -61,7 +61,7 @@ pre, .rustdoc.source .example-wrap { background-color: #14191f; } -.rust-logo > img { +.rust-logo { filter: drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) @@ -222,7 +222,7 @@ nav.main .separator { a { color: #39AFD7; } -a.srclink, + a#toggle-all-docs, a.anchor, .small-section-header a, diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css index 64b6eb6696b..2db725c9b4e 100644 --- a/src/librustdoc/html/static/css/themes/dark.css +++ b/src/librustdoc/html/static/css/themes/dark.css @@ -32,7 +32,7 @@ pre, .rustdoc.source .example-wrap { background-color: #505050; } -.rust-logo > img { +.rust-logo { filter: drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) @@ -180,7 +180,7 @@ nav.main .separator { a { color: #D2991D; } -a.srclink, + a#toggle-all-docs, a.anchor, .small-section-header a, diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css index dbacc9f3073..3c8dbeb98c5 100644 --- a/src/librustdoc/html/static/css/themes/light.css +++ b/src/librustdoc/html/static/css/themes/light.css @@ -43,7 +43,7 @@ pre, .rustdoc.source .example-wrap { scrollbar-color: rgba(36, 37, 39, 0.6) #d9d9d9; } -.rust-logo > img { +.rust-logo { /* No need for a border in here! */ } @@ -177,7 +177,7 @@ nav.main .separator { a { color: #3873AD; } -a.srclink, + a#toggle-all-docs, a.anchor, .small-section-header a, @@ -243,10 +243,6 @@ details.undocumented > summary::before { border-color: #bfbfbf; } -.since { - color: grey; -} - .result-name .primitive > i, .result-name .keyword > i { color: black; } diff --git a/src/librustdoc/html/static/images/favicon-16x16.png b/src/librustdoc/html/static/images/favicon-16x16.png index 7cfe6c13550..ea4b45cae16 100644 --- a/src/librustdoc/html/static/images/favicon-16x16.png +++ b/src/librustdoc/html/static/images/favicon-16x16.png Binary files differdiff --git a/src/librustdoc/html/static/images/favicon-32x32.png b/src/librustdoc/html/static/images/favicon-32x32.png index 5109c1de8be..69b8613ce15 100644 --- a/src/librustdoc/html/static/images/favicon-32x32.png +++ b/src/librustdoc/html/static/images/favicon-32x32.png Binary files differdiff --git a/src/librustdoc/html/static/images/rust-logo.png b/src/librustdoc/html/static/images/rust-logo.png deleted file mode 100644 index 74b4bd69504..00000000000 --- a/src/librustdoc/html/static/images/rust-logo.png +++ /dev/null Binary files differdiff --git a/src/librustdoc/html/static/images/rust-logo.svg b/src/librustdoc/html/static/images/rust-logo.svg new file mode 100644 index 00000000000..62424d8ffd7 --- /dev/null +++ b/src/librustdoc/html/static/images/rust-logo.svg @@ -0,0 +1,61 @@ +<svg version="1.1" height="106" width="106" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g id="logo" transform="translate(53, 53)"> + <path id="r" transform="translate(0.5, 0.5)" stroke="black" stroke-width="1" stroke-linejoin="round" d=" + M -9,-15 H 4 C 12,-15 12,-7 4,-7 H -9 Z + M -40,22 H 0 V 11 H -9 V 3 H 1 C 12,3 6,22 15,22 H 40 + V 3 H 34 V 5 C 34,13 25,12 24,7 C 23,2 19,-2 18,-2 C 33,-10 24,-26 12,-26 H -35 + V -15 H -25 V 11 H -40 Z" /> + <g id="gear" mask="url(#holes)"> + <circle r="43" fill="none" stroke="black" stroke-width="9" /> + <g id="cogs"> + <polygon id="cog" stroke="black" stroke-width="3" stroke-linejoin="round" points="46,3 51,0 46,-3" /> + <use xlink:href="#cog" transform="rotate(11.25)" /> + <use xlink:href="#cog" transform="rotate(22.50)" /> + <use xlink:href="#cog" transform="rotate(33.75)" /> + <use xlink:href="#cog" transform="rotate(45.00)" /> + <use xlink:href="#cog" transform="rotate(56.25)" /> + <use xlink:href="#cog" transform="rotate(67.50)" /> + <use xlink:href="#cog" transform="rotate(78.75)" /> + <use xlink:href="#cog" transform="rotate(90.00)" /> + <use xlink:href="#cog" transform="rotate(101.25)" /> + <use xlink:href="#cog" transform="rotate(112.50)" /> + <use xlink:href="#cog" transform="rotate(123.75)" /> + <use xlink:href="#cog" transform="rotate(135.00)" /> + <use xlink:href="#cog" transform="rotate(146.25)" /> + <use xlink:href="#cog" transform="rotate(157.50)" /> + <use xlink:href="#cog" transform="rotate(168.75)" /> + <use xlink:href="#cog" transform="rotate(180.00)" /> + <use xlink:href="#cog" transform="rotate(191.25)" /> + <use xlink:href="#cog" transform="rotate(202.50)" /> + <use xlink:href="#cog" transform="rotate(213.75)" /> + <use xlink:href="#cog" transform="rotate(225.00)" /> + <use xlink:href="#cog" transform="rotate(236.25)" /> + <use xlink:href="#cog" transform="rotate(247.50)" /> + <use xlink:href="#cog" transform="rotate(258.75)" /> + <use xlink:href="#cog" transform="rotate(270.00)" /> + <use xlink:href="#cog" transform="rotate(281.25)" /> + <use xlink:href="#cog" transform="rotate(292.50)" /> + <use xlink:href="#cog" transform="rotate(303.75)" /> + <use xlink:href="#cog" transform="rotate(315.00)" /> + <use xlink:href="#cog" transform="rotate(326.25)" /> + <use xlink:href="#cog" transform="rotate(337.50)" /> + <use xlink:href="#cog" transform="rotate(348.75)" /> + </g> + <g id="mounts"> + <polygon id="mount" stroke="black" stroke-width="6" stroke-linejoin="round" points="-7,-42 0,-35 7,-42" /> + <use xlink:href="#mount" transform="rotate(72)" /> + <use xlink:href="#mount" transform="rotate(144)" /> + <use xlink:href="#mount" transform="rotate(216)" /> + <use xlink:href="#mount" transform="rotate(288)" /> + </g> + </g> + <mask id="holes"> + <rect x="-60" y="-60" width="120" height="120" fill="white"/> + <circle id="hole" cy="-40" r="3" /> + <use xlink:href="#hole" transform="rotate(72)" /> + <use xlink:href="#hole" transform="rotate(144)" /> + <use xlink:href="#hole" transform="rotate(216)" /> + <use xlink:href="#hole" transform="rotate(288)" /> + </mask> +</g> +</svg> diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index f81f6d5d61f..f41c1bd817a 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -289,8 +289,8 @@ function hideThemeButtonState() { var params = searchState.getQueryStringParams(); if (params.search !== undefined) { var search = searchState.outputElement(); - search.innerHTML = "<h3 style=\"text-align: center;\">" + - searchState.loadingText + "</h3>"; + search.innerHTML = "<h3 class=\"search-loading\">" + + searchState.loadingText + "</h3>"; searchState.showResults(search); loadSearch(); } diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index cf320f7b495..e859431e1f1 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1085,7 +1085,7 @@ window.initSearch = function(rawSearchIndex) { return "<button>" + text + " <div class=\"count\">(" + nbElems + ")</div></button>"; } - function showResults(results, go_to_first) { + function showResults(results, go_to_first, filterCrates) { var search = searchState.outputElement(); if (go_to_first || (results.others.length === 1 && getSettingValue("go-to-only-result") === "true" @@ -1126,9 +1126,16 @@ window.initSearch = function(rawSearchIndex) { } } - var output = "<h1>Results for " + escape(query.query) + + let crates = `<select id="crate-search"><option value="All crates">All crates</option>`; + for (let c of window.ALL_CRATES) { + crates += `<option value="${c}" ${c == filterCrates && "selected"}>${c}</option>`; + } + crates += `</select>`; + var output = `<div id="search-settings"> + <h1 class="search-results-title">Results for ${escape(query.query)} ` + (query.type ? " (type: " + escape(query.type) + ")" : "") + "</h1>" + - "<div id=\"titles\">" + + ` in ${crates} ` + + `</div><div id="titles">` + makeTabHeader(0, "In Names", ret_others[1]) + makeTabHeader(1, "In Parameters", ret_in_args[1]) + makeTabHeader(2, "In Return Types", ret_returned[1]) + @@ -1141,6 +1148,7 @@ window.initSearch = function(rawSearchIndex) { resultsElem.appendChild(ret_returned[0]); search.innerHTML = output; + document.getElementById("crate-search").addEventListener("input", updateCrate); search.appendChild(resultsElem); // Reset focused elements. searchState.focusedByTab = [null, null, null]; @@ -1316,7 +1324,8 @@ window.initSearch = function(rawSearchIndex) { } var filterCrates = getFilterCrates(); - showResults(execSearch(query, searchWords, filterCrates), params["go_to_first"]); + showResults(execSearch(query, searchWords, filterCrates), + params["go_to_first"], filterCrates); } function buildIndex(rawSearchIndex) { @@ -1552,19 +1561,6 @@ window.initSearch = function(rawSearchIndex) { } }); - - var selectCrate = document.getElementById("crate-search"); - if (selectCrate) { - selectCrate.onchange = function() { - updateLocalStorage("rustdoc-saved-filter-crate", selectCrate.value); - // In case you "cut" the entry from the search input, then change the crate filter - // before paste back the previous search, you get the old search results without - // the filter. To prevent this, we need to remove the previous results. - currentResults = null; - search(undefined, true); - }; - } - // Push and pop states are used to add search results to the browser // history. if (searchState.browserSupportsHistoryApi()) { @@ -1616,6 +1612,15 @@ window.initSearch = function(rawSearchIndex) { }; } + function updateCrate(ev) { + updateLocalStorage("rustdoc-saved-filter-crate", ev.target.value); + // In case you "cut" the entry from the search input, then change the crate filter + // before paste back the previous search, you get the old search results without + // the filter. To prevent this, we need to remove the previous results. + currentResults = null; + search(undefined, true); + } + searchWords = buildIndex(rawSearchIndex); registerSearchEvents(); // If there's a search term in the URL, execute the search now. diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs index 56c5399d074..cd369a93d82 100644 --- a/src/librustdoc/html/static_files.rs +++ b/src/librustdoc/html/static_files.rs @@ -67,8 +67,9 @@ crate static LICENSE_APACHE: &[u8] = include_bytes!("static/LICENSE-APACHE.txt") /// The contents of `LICENSE-MIT.txt`, the text of the MIT License. crate static LICENSE_MIT: &[u8] = include_bytes!("static/LICENSE-MIT.txt"); -/// The contents of `rust-logo.png`, the default icon of the documentation. -crate static RUST_LOGO: &[u8] = include_bytes!("static/images/rust-logo.png"); +/// The contents of `rust-logo.svg`, the default icon of the documentation. +crate static RUST_LOGO_SVG: &[u8] = include_bytes!("static/images/rust-logo.svg"); + /// The default documentation favicons (SVG and PNG fallbacks) crate static RUST_FAVICON_SVG: &[u8] = include_bytes!("static/images/favicon.svg"); crate static RUST_FAVICON_PNG_16: &[u8] = include_bytes!("static/images/favicon-16x16.png"); diff --git a/src/librustdoc/html/templates/print_item.html b/src/librustdoc/html/templates/print_item.html deleted file mode 100644 index 5a468f3cc1e..00000000000 --- a/src/librustdoc/html/templates/print_item.html +++ /dev/null @@ -1,26 +0,0 @@ -<h1 class="fqn"> {#- -#} - <span class="in-band"> {#- -#} - {{-typ-}} - {#- The breadcrumbs of the item path, like std::string -#} - {%- for component in path_components -%} - <a href="{{component.path | safe}}index.html">{{component.name}}</a>::<wbr> - {%- endfor -%} - <a class="{{item_type}}" href="#">{{name}}</a> {#- -#} - <button id="copy-path" onclick="copy_path(this)" title="Copy item path to clipboard"> {#- -#} - <img src="{{static_root_path | safe}}clipboard{{page.resource_suffix}}.svg" {# -#} - width="19" height="18" {# -#} - alt="Copy item path"> {#- -#} - </button> {#- -#} - </span> {#- -#} - <span class="out-of-band"> {#- -#} - {{- stability_since_raw | safe -}} - <span id="render-detail"> {#- -#} - <a id="toggle-all-docs" href="javascript:void(0)" title="collapse all docs"> {#- -#} - [<span class="inner">−</span>] {#- -#} - </a> {#- -#} - </span> {#- -#} - {%- if src_href -%} - <a class="srclink" href="{{src_href | safe}}" title="goto source code">[src]</a> - {%- endif -%} - </span> {#- -#} -</h1> {#- -#} diff --git a/src/librustdoc/html/tests.rs b/src/librustdoc/html/tests.rs index dee9f5e5038..437d3995e29 100644 --- a/src/librustdoc/html/tests.rs +++ b/src/librustdoc/html/tests.rs @@ -1,44 +1,50 @@ use crate::html::format::href_relative_parts; +use rustc_span::{sym, Symbol}; -fn assert_relative_path(expected: &str, relative_to_fqp: &[&str], fqp: &[&str]) { - let relative_to_fqp: Vec<String> = relative_to_fqp.iter().copied().map(String::from).collect(); - let fqp: Vec<String> = fqp.iter().copied().map(String::from).collect(); - assert_eq!(expected, href_relative_parts(&fqp, &relative_to_fqp).finish()); +fn assert_relative_path(expected: &[Symbol], relative_to_fqp: &[Symbol], fqp: &[Symbol]) { + // No `create_default_session_globals_then` call is needed here because all + // the symbols used are static, and no `Symbol::intern` calls occur. + assert_eq!(expected, href_relative_parts(&fqp, &relative_to_fqp).collect::<Vec<_>>()); } #[test] fn href_relative_parts_basic() { - let relative_to_fqp = &["std", "vec"]; - let fqp = &["std", "iter"]; - assert_relative_path("../iter", relative_to_fqp, fqp); + let relative_to_fqp = &[sym::std, sym::vec]; + let fqp = &[sym::std, sym::iter]; + assert_relative_path(&[sym::dotdot, sym::iter], relative_to_fqp, fqp); } + #[test] fn href_relative_parts_parent_module() { - let relative_to_fqp = &["std", "vec"]; - let fqp = &["std"]; - assert_relative_path("..", relative_to_fqp, fqp); + let relative_to_fqp = &[sym::std, sym::vec]; + let fqp = &[sym::std]; + assert_relative_path(&[sym::dotdot], relative_to_fqp, fqp); } + #[test] fn href_relative_parts_different_crate() { - let relative_to_fqp = &["std", "vec"]; - let fqp = &["core", "iter"]; - assert_relative_path("../../core/iter", relative_to_fqp, fqp); + let relative_to_fqp = &[sym::std, sym::vec]; + let fqp = &[sym::core, sym::iter]; + assert_relative_path(&[sym::dotdot, sym::dotdot, sym::core, sym::iter], relative_to_fqp, fqp); } + #[test] fn href_relative_parts_same_module() { - let relative_to_fqp = &["std", "vec"]; - let fqp = &["std", "vec"]; - assert_relative_path("", relative_to_fqp, fqp); + let relative_to_fqp = &[sym::std, sym::vec]; + let fqp = &[sym::std, sym::vec]; + assert_relative_path(&[], relative_to_fqp, fqp); } + #[test] fn href_relative_parts_child_module() { - let relative_to_fqp = &["std"]; - let fqp = &["std", "vec"]; - assert_relative_path("vec", relative_to_fqp, fqp); + let relative_to_fqp = &[sym::std]; + let fqp = &[sym::std, sym::vec]; + assert_relative_path(&[sym::vec], relative_to_fqp, fqp); } + #[test] fn href_relative_parts_root() { let relative_to_fqp = &[]; - let fqp = &["std"]; - assert_relative_path("std", relative_to_fqp, fqp); + let fqp = &[sym::std]; + assert_relative_path(&[sym::std], relative_to_fqp, fqp); } diff --git a/src/librustdoc/html/url_parts_builder.rs b/src/librustdoc/html/url_parts_builder.rs index 918d5e6bd1b..2bb78aa7dc9 100644 --- a/src/librustdoc/html/url_parts_builder.rs +++ b/src/librustdoc/html/url_parts_builder.rs @@ -1,3 +1,7 @@ +use std::fmt::{self, Write}; + +use rustc_span::Symbol; + /// A builder that allows efficiently and easily constructing the part of a URL /// after the domain: `nightly/core/str/struct.Bytes.html`. /// @@ -10,6 +14,7 @@ crate struct UrlPartsBuilder { impl UrlPartsBuilder { /// Create an empty buffer. + #[allow(dead_code)] crate fn new() -> Self { Self { buf: String::new() } } @@ -62,6 +67,26 @@ impl UrlPartsBuilder { self.buf.push_str(part); } + /// Push a component onto the buffer, using [`format!`]'s formatting syntax. + /// + /// # Examples + /// + /// Basic usage (equivalent to the example for [`UrlPartsBuilder::push`]): + /// + /// ```ignore (private-type) + /// let mut builder = UrlPartsBuilder::new(); + /// builder.push("core"); + /// builder.push("str"); + /// builder.push_fmt(format_args!("{}.{}.html", "struct", "Bytes")); + /// assert_eq!(builder.finish(), "core/str/struct.Bytes.html"); + /// ``` + crate fn push_fmt(&mut self, args: fmt::Arguments<'_>) { + if !self.buf.is_empty() { + self.buf.push('/'); + } + self.buf.write_fmt(args).unwrap() + } + /// Push a component onto the front of the buffer. /// /// # Examples @@ -93,10 +118,25 @@ impl UrlPartsBuilder { /// This is just a guess at the average length of a URL part, /// used for [`String::with_capacity`] calls in the [`FromIterator`] -/// and [`Extend`] impls. +/// and [`Extend`] impls, and for [estimating item path lengths]. /// -/// This is intentionally on the lower end to avoid overallocating. -const AVG_PART_LENGTH: usize = 5; +/// The value `8` was chosen for two main reasons: +/// +/// * It seems like a good guess for the average part length. +/// * jemalloc's size classes are all multiples of eight, +/// which means that the amount of memory it allocates will often match +/// the amount requested, avoiding wasted bytes. +/// +/// [estimating item path lengths]: estimate_item_path_byte_length +const AVG_PART_LENGTH: usize = 8; + +/// Estimate the number of bytes in an item's path, based on how many segments it has. +/// +/// **Note:** This is only to be used with, e.g., [`String::with_capacity()`]; +/// the return value is just a rough estimate. +crate const fn estimate_item_path_byte_length(segment_count: usize) -> usize { + AVG_PART_LENGTH * segment_count +} impl<'a> FromIterator<&'a str> for UrlPartsBuilder { fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self { @@ -115,5 +155,26 @@ impl<'a> Extend<&'a str> for UrlPartsBuilder { } } +impl FromIterator<Symbol> for UrlPartsBuilder { + fn from_iter<T: IntoIterator<Item = Symbol>>(iter: T) -> Self { + // This code has to be duplicated from the `&str` impl because of + // `Symbol::as_str`'s lifetimes. + let iter = iter.into_iter(); + let mut builder = Self::with_capacity_bytes(AVG_PART_LENGTH * iter.size_hint().0); + iter.for_each(|part| builder.push(part.as_str())); + builder + } +} + +impl Extend<Symbol> for UrlPartsBuilder { + fn extend<T: IntoIterator<Item = Symbol>>(&mut self, iter: T) { + // This code has to be duplicated from the `&str` impl because of + // `Symbol::as_str`'s lifetimes. + let iter = iter.into_iter(); + self.buf.reserve(AVG_PART_LENGTH * iter.size_hint().0); + iter.for_each(|part| self.push(part.as_str())); + } +} + #[cfg(test)] mod tests; diff --git a/src/librustdoc/html/url_parts_builder/tests.rs b/src/librustdoc/html/url_parts_builder/tests.rs index 43338c95010..636e1ab5527 100644 --- a/src/librustdoc/html/url_parts_builder/tests.rs +++ b/src/librustdoc/html/url_parts_builder/tests.rs @@ -41,6 +41,16 @@ fn push_front_non_empty() { } #[test] +fn push_fmt() { + let mut builder = UrlPartsBuilder::new(); + builder.push_fmt(format_args!("{}", "core")); + builder.push("str"); + builder.push_front("nightly"); + builder.push_fmt(format_args!("{}.{}.html", "struct", "Bytes")); + t(builder, "nightly/core/str/struct.Bytes.html"); +} + +#[test] fn collect() { t(["core", "str"].into_iter().collect(), "core/str"); t(["core", "str", "struct.Bytes.html"].into_iter().collect(), "core/str/struct.Bytes.html"); diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index 88a5c0c5ca2..81fbfd9fdbd 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -120,7 +120,7 @@ impl<'tcx> JsonRenderer<'tcx> { }) .0 .last() - .map(Clone::clone), + .map(|s| s.to_string()), visibility: types::Visibility::Public, inner: types::ItemEnum::Trait(trait_item.clone().into_tcx(self.tcx)), span: None, @@ -230,7 +230,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { from_item_id(k.into()), types::ItemSummary { crate_id: k.krate.as_u32(), - path, + path: path.iter().map(|s| s.to_string()).collect(), kind: kind.into_tcx(self.tcx), }, ) diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 014ac484dcf..a647a0fbfa5 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -18,6 +18,7 @@ #![feature(iter_intersperse)] #![recursion_limit = "256"] #![warn(rustc::internal)] +#![allow(clippy::collapsible_if, clippy::collapsible_else_if)] #[macro_use] extern crate tracing; @@ -82,6 +83,7 @@ use rustc_session::getopts; use rustc_session::{early_error, early_warn}; use crate::clean::utils::DOC_RUST_LANG_ORG_CHANNEL; +use crate::passes::collect_intra_doc_links; /// A macro to create a FxHashMap. /// @@ -798,7 +800,15 @@ fn main_options(options: config::Options) -> MainResult { // We need to hold on to the complete resolver, so we cause everything to be // cloned for the analysis passes to use. Suboptimal, but necessary in the // current architecture. - let resolver = core::create_resolver(externs, queries, sess); + // FIXME(#83761): Resolver cloning can lead to inconsistencies between data in the + // two copies because one of the copies can be modified after `TyCtxt` construction. + let (resolver, resolver_caches) = { + let (krate, resolver, _) = &*abort_on_err(queries.expansion(), sess).peek(); + let resolver_caches = resolver.borrow_mut().access(|resolver| { + collect_intra_doc_links::early_resolve_intra_doc_links(resolver, krate, externs) + }); + (resolver.clone(), resolver_caches) + }; if sess.diagnostic().has_errors_or_lint_errors() { sess.fatal("Compilation failed, aborting rustdoc"); @@ -811,6 +821,7 @@ fn main_options(options: config::Options) -> MainResult { core::run_global_ctxt( tcx, resolver, + resolver_caches, show_coverage, render_options, output_format, diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 79530086282..af62232e792 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -13,7 +13,7 @@ use rustc_hir::def::{ PerNS, }; use rustc_hir::def_id::{CrateNum, DefId}; -use rustc_middle::ty::{Ty, TyCtxt}; +use rustc_middle::ty::{DefIdTree, Ty, TyCtxt}; use rustc_middle::{bug, span_bug, ty}; use rustc_resolve::ParentScope; use rustc_session::lint::Lint; @@ -25,8 +25,8 @@ use smallvec::{smallvec, SmallVec}; use pulldown_cmark::LinkType; use std::borrow::Cow; -use std::cell::Cell; use std::convert::{TryFrom, TryInto}; +use std::fmt::Write; use std::mem; use std::ops::Range; @@ -38,7 +38,7 @@ use crate::passes::Pass; use crate::visit::DocVisitor; mod early; -crate use early::load_intra_link_crates; +crate use early::early_resolve_intra_doc_links; crate const COLLECT_INTRA_DOC_LINKS: Pass = Pass { name: "collect-intra-doc-links", @@ -47,12 +47,8 @@ crate const COLLECT_INTRA_DOC_LINKS: Pass = Pass { }; fn collect_intra_doc_links(krate: Crate, cx: &mut DocContext<'_>) -> Crate { - let mut collector = LinkCollector { - cx, - mod_ids: Vec::new(), - kind_side_channel: Cell::new(None), - visited_links: FxHashMap::default(), - }; + let mut collector = + LinkCollector { cx, mod_ids: Vec::new(), visited_links: FxHashMap::default() }; collector.visit_crate(&krate); krate } @@ -240,53 +236,73 @@ enum AnchorFailure { #[derive(Clone, Debug, Hash, PartialEq, Eq)] crate enum UrlFragment { - Method(Symbol), - TyMethod(Symbol), - AssociatedConstant(Symbol), - AssociatedType(Symbol), - - StructField(Symbol), - Variant(Symbol), - VariantField { variant: Symbol, field: Symbol }, - + Item(ItemFragment), UserWritten(String), } impl UrlFragment { + /// Render the fragment, including the leading `#`. + crate fn render(&self, s: &mut String, tcx: TyCtxt<'_>) -> std::fmt::Result { + match self { + UrlFragment::Item(frag) => frag.render(s, tcx), + UrlFragment::UserWritten(raw) => write!(s, "#{}", raw), + } + } +} + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +crate struct ItemFragment(FragmentKind, DefId); + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +crate enum FragmentKind { + Method, + TyMethod, + AssociatedConstant, + AssociatedType, + + StructField, + Variant, + VariantField, +} + +impl ItemFragment { /// Create a fragment for an associated item. /// /// `is_prototype` is whether this associated item is a trait method /// without a default definition. - fn from_assoc_item(name: Symbol, kind: ty::AssocKind, is_prototype: bool) -> Self { + fn from_assoc_item(def_id: DefId, kind: ty::AssocKind, is_prototype: bool) -> Self { match kind { ty::AssocKind::Fn => { if is_prototype { - UrlFragment::TyMethod(name) + ItemFragment(FragmentKind::TyMethod, def_id) } else { - UrlFragment::Method(name) + ItemFragment(FragmentKind::Method, def_id) } } - ty::AssocKind::Const => UrlFragment::AssociatedConstant(name), - ty::AssocKind::Type => UrlFragment::AssociatedType(name), + ty::AssocKind::Const => ItemFragment(FragmentKind::AssociatedConstant, def_id), + ty::AssocKind::Type => ItemFragment(FragmentKind::AssociatedType, def_id), } } -} -/// Render the fragment, including the leading `#`. -impl std::fmt::Display for UrlFragment { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "#")?; - match self { - UrlFragment::Method(name) => write!(f, "method.{}", name), - UrlFragment::TyMethod(name) => write!(f, "tymethod.{}", name), - UrlFragment::AssociatedConstant(name) => write!(f, "associatedconstant.{}", name), - UrlFragment::AssociatedType(name) => write!(f, "associatedtype.{}", name), - UrlFragment::StructField(name) => write!(f, "structfield.{}", name), - UrlFragment::Variant(name) => write!(f, "variant.{}", name), - UrlFragment::VariantField { variant, field } => { - write!(f, "variant.{}.field.{}", variant, field) + /// Render the fragment, including the leading `#`. + crate fn render(&self, s: &mut String, tcx: TyCtxt<'_>) -> std::fmt::Result { + write!(s, "#")?; + match *self { + ItemFragment(kind, def_id) => { + let name = tcx.item_name(def_id); + match kind { + FragmentKind::Method => write!(s, "method.{}", name), + FragmentKind::TyMethod => write!(s, "tymethod.{}", name), + FragmentKind::AssociatedConstant => write!(s, "associatedconstant.{}", name), + FragmentKind::AssociatedType => write!(s, "associatedtype.{}", name), + FragmentKind::StructField => write!(s, "structfield.{}", name), + FragmentKind::Variant => write!(s, "variant.{}", name), + FragmentKind::VariantField => { + let variant = tcx.item_name(tcx.parent(def_id).unwrap()); + write!(s, "variant.{}.field.{}", variant, name) + } + } } - UrlFragment::UserWritten(raw) => write!(f, "{}", raw), } } } @@ -296,7 +312,7 @@ struct ResolutionInfo { module_id: DefId, dis: Option<Disambiguator>, path_str: String, - extra_fragment: Option<UrlFragment>, + extra_fragment: Option<String>, } #[derive(Clone)] @@ -310,7 +326,6 @@ struct DiagnosticInfo<'a> { #[derive(Clone, Debug, Hash)] struct CachedLink { pub res: (Res, Option<UrlFragment>), - pub side_channel: Option<(DefKind, DefId)>, } struct LinkCollector<'a, 'tcx> { @@ -320,10 +335,6 @@ struct LinkCollector<'a, 'tcx> { /// The last module will be used if the parent scope of the current item is /// unknown. mod_ids: Vec<DefId>, - /// This is used to store the kind of associated items, - /// because `clean` and the disambiguator code expect them to be different. - /// See the code for associated items on inherent impls for details. - kind_side_channel: Cell<Option<(DefKind, DefId)>>, /// Cache the resolved links so we can avoid resolving (and emitting errors for) the same link. /// The link will be `None` if it could not be resolved (i.e. the error was cached). visited_links: FxHashMap<ResolutionInfo, Option<CachedLink>>, @@ -340,7 +351,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { &self, path_str: &'path str, module_id: DefId, - ) -> Result<(Res, Option<UrlFragment>), ErrorKind<'path>> { + ) -> Result<(Res, Option<ItemFragment>), ErrorKind<'path>> { let tcx = self.cx.tcx; let no_res = || ResolutionFailure::NotResolved { module_id, @@ -387,14 +398,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } match tcx.type_of(did).kind() { ty::Adt(def, _) if def.is_enum() => { - if def.all_fields().any(|item| item.ident.name == variant_field_name) { - Ok(( - ty_res, - Some(UrlFragment::VariantField { - variant: variant_name, - field: variant_field_name, - }), - )) + if let Some(field) = + def.all_fields().find(|f| f.ident(tcx).name == variant_field_name) + { + Ok((ty_res, Some(ItemFragment(FragmentKind::VariantField, field.did)))) } else { Err(ResolutionFailure::NotResolved { module_id, @@ -422,7 +429,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { prim_ty: PrimitiveType, ns: Namespace, item_name: Symbol, - ) -> Option<(Res, UrlFragment, Option<(DefKind, DefId)>)> { + ) -> Option<(Res, ItemFragment)> { let tcx = self.cx.tcx; prim_ty.impls(tcx).into_iter().find_map(|&impl_| { @@ -430,8 +437,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { .find_by_name_and_namespace(tcx, Ident::with_dummy_span(item_name), ns, impl_) .map(|item| { let kind = item.kind; - let fragment = UrlFragment::from_assoc_item(item_name, kind, false); - (Res::Primitive(prim_ty), fragment, Some((kind.as_def_kind(), item.def_id))) + let fragment = ItemFragment::from_assoc_item(item.def_id, kind, false); + (Res::Primitive(prim_ty), fragment) }) }) } @@ -505,8 +512,30 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { path_str: &'path str, ns: Namespace, module_id: DefId, - extra_fragment: &Option<UrlFragment>, + user_fragment: &Option<String>, ) -> Result<(Res, Option<UrlFragment>), ErrorKind<'path>> { + let (res, rustdoc_fragment) = self.resolve_inner(path_str, ns, module_id)?; + let chosen_fragment = match (user_fragment, rustdoc_fragment) { + (Some(_), Some(r_frag)) => { + let diag_res = match r_frag { + ItemFragment(_, did) => Res::Def(self.cx.tcx.def_kind(did), did), + }; + let failure = AnchorFailure::RustdocAnchorConflict(diag_res); + return Err(ErrorKind::AnchorFailure(failure)); + } + (Some(u_frag), None) => Some(UrlFragment::UserWritten(u_frag.clone())), + (None, Some(r_frag)) => Some(UrlFragment::Item(r_frag)), + (None, None) => None, + }; + Ok((res, chosen_fragment)) + } + + fn resolve_inner<'path>( + &mut self, + path_str: &'path str, + ns: Namespace, + module_id: DefId, + ) -> Result<(Res, Option<ItemFragment>), ErrorKind<'path>> { if let Some(res) = self.resolve_path(path_str, ns, module_id) { match res { // FIXME(#76467): make this fallthrough to lookup the associated @@ -514,10 +543,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { Res::Def(DefKind::AssocFn | DefKind::AssocConst, _) => assert_eq!(ns, ValueNS), Res::Def(DefKind::AssocTy, _) => assert_eq!(ns, TypeNS), Res::Def(DefKind::Variant, _) => { - return handle_variant(self.cx, res, extra_fragment); + return handle_variant(self.cx, res); } // Not a trait item; just return what we found. - _ => return Ok((res, extra_fragment.clone())), + _ => return Ok((res, None)), } } @@ -548,23 +577,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { resolve_primitive(&path_root, TypeNS) .or_else(|| self.resolve_path(&path_root, TypeNS, module_id)) .and_then(|ty_res| { - let (res, fragment, side_channel) = + let (res, fragment) = self.resolve_associated_item(ty_res, item_name, ns, module_id)?; - let result = if extra_fragment.is_some() { - // NOTE: can never be a primitive since `side_channel.is_none()` only when `res` - // is a trait (and the side channel DefId is always an associated item). - let diag_res = side_channel.map_or(res, |(k, r)| Res::Def(k, r)); - Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(diag_res))) - } else { - // HACK(jynelson): `clean` expects the type, not the associated item - // but the disambiguator logic expects the associated item. - // Store the kind in a side channel so that only the disambiguator logic looks at it. - if let Some((kind, id)) = side_channel { - self.kind_side_channel.set(Some((kind, id))); - } - Ok((res, Some(fragment))) - }; - Some(result) + + Some(Ok((res, Some(fragment)))) }) .unwrap_or_else(|| { if ns == Namespace::ValueNS { @@ -661,7 +677,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { item_name: Symbol, ns: Namespace, module_id: DefId, - ) -> Option<(Res, UrlFragment, Option<(DefKind, DefId)>)> { + ) -> Option<(Res, ItemFragment)> { let tcx = self.cx.tcx; match root_res { @@ -676,11 +692,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { assoc_item.map(|item| { let kind = item.kind; - let fragment = UrlFragment::from_assoc_item(item_name, kind, false); - // HACK(jynelson): `clean` expects the type, not the associated item - // but the disambiguator logic expects the associated item. - // Store the kind in a side channel so that only the disambiguator logic looks at it. - (root_res, fragment, Some((kind.as_def_kind(), item.def_id))) + let fragment = ItemFragment::from_assoc_item(item.def_id, kind, false); + (root_res, fragment) }) }) } @@ -730,11 +743,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { if let Some(item) = assoc_item { let kind = item.kind; - let fragment = UrlFragment::from_assoc_item(item_name, kind, false); - // HACK(jynelson): `clean` expects the type, not the associated item - // but the disambiguator logic expects the associated item. - // Store the kind in a side channel so that only the disambiguator logic looks at it. - return Some((root_res, fragment, Some((kind.as_def_kind(), item.def_id)))); + let fragment = ItemFragment::from_assoc_item(item.def_id, kind, false); + return Some((root_res, fragment)); } if ns != Namespace::ValueNS { @@ -764,24 +774,20 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { .non_enum_variant() .fields .iter() - .find(|item| item.ident.name == item_name)?; - Some(( - root_res, - UrlFragment::StructField(field.ident.name), - Some((DefKind::Field, field.did)), - )) + .find(|item| item.ident(tcx).name == item_name)?; + Some((root_res, ItemFragment(FragmentKind::StructField, field.did))) } Res::Def(DefKind::Trait, did) => tcx .associated_items(did) .find_by_name_and_namespace(tcx, Ident::with_dummy_span(item_name), ns, did) .map(|item| { - let fragment = UrlFragment::from_assoc_item( - item_name, + let fragment = ItemFragment::from_assoc_item( + item.def_id, item.kind, !item.defaultness.has_value(), ); let res = Res::Def(item.kind.as_def_kind(), item.def_id); - (res, fragment, None) + (res, fragment) }), _ => None, } @@ -798,23 +804,32 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { ns: Namespace, path_str: &str, module_id: DefId, - extra_fragment: &Option<UrlFragment>, + extra_fragment: &Option<String>, ) -> Option<Res> { // resolve() can't be used for macro namespace let result = match ns { - Namespace::MacroNS => self.resolve_macro(path_str, module_id).map_err(ErrorKind::from), + Namespace::MacroNS => self + .resolve_macro(path_str, module_id) + .map(|res| (res, None)) + .map_err(ErrorKind::from), Namespace::TypeNS | Namespace::ValueNS => { - self.resolve(path_str, ns, module_id, extra_fragment).map(|(res, _)| res) + self.resolve(path_str, ns, module_id, extra_fragment) } }; let res = match result { - Ok(res) => Some(res), + Ok((res, frag)) => { + if let Some(UrlFragment::Item(ItemFragment(_, id))) = frag { + Some(Res::Def(self.cx.tcx.def_kind(id), id)) + } else { + Some(res) + } + } Err(ErrorKind::Resolve(box kind)) => kind.full_res(), Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(res))) => Some(res), Err(ErrorKind::AnchorFailure(AnchorFailure::MultipleAnchors)) => None, }; - self.kind_side_channel.take().map(|(kind, id)| Res::Def(kind, id)).or(res) + res } } @@ -912,8 +927,6 @@ fn is_derive_trait_collision<T>(ns: &PerNS<Result<(Res, T), ResolutionFailure<'_ impl<'a, 'tcx> DocVisitor for LinkCollector<'a, 'tcx> { fn visit_item(&mut self, item: &Item) { - use rustc_middle::ty::DefIdTree; - let parent_node = item.def_id.as_def_id().and_then(|did| find_nearest_parent_module(self.cx.tcx, did)); if parent_node.is_some() { @@ -1033,7 +1046,7 @@ impl From<AnchorFailure> for PreprocessingError<'_> { struct PreprocessingInfo { path_str: String, disambiguator: Option<Disambiguator>, - extra_fragment: Option<UrlFragment>, + extra_fragment: Option<String>, link_text: String, } @@ -1119,7 +1132,7 @@ fn preprocess_link<'a>( Some(Ok(PreprocessingInfo { path_str, disambiguator, - extra_fragment: extra_fragment.map(|frag| UrlFragment::UserWritten(frag.to_owned())), + extra_fragment: extra_fragment.map(|frag| frag.to_owned()), link_text: link_text.to_owned(), })) } @@ -1286,7 +1299,11 @@ impl LinkCollector<'_, '_> { }; let verify = |kind: DefKind, id: DefId| { - let (kind, id) = self.kind_side_channel.take().unwrap_or((kind, id)); + let (kind, id) = if let Some(UrlFragment::Item(ItemFragment(_, id))) = fragment { + (self.cx.tcx.def_kind(id), id) + } else { + (kind, id) + }; debug!("intra-doc link to {} resolved to {:?} (id: {:?})", path_str, res, id); // Disallow e.g. linking to enums with `struct@` @@ -1330,7 +1347,9 @@ impl LinkCollector<'_, '_> { match res { Res::Primitive(prim) => { - if let Some((kind, id)) = self.kind_side_channel.take() { + if let Some(UrlFragment::Item(ItemFragment(_, id))) = fragment { + let kind = self.cx.tcx.def_kind(id); + // We're actually resolving an associated item of a primitive, so we need to // verify the disambiguator (if any) matches the type of the associated item. // This case should really follow the same flow as the `Res::Def` branch below, @@ -1347,22 +1366,7 @@ impl LinkCollector<'_, '_> { && item.def_id.is_local() && !self.cx.tcx.features().intra_doc_pointers { - let span = super::source_span_for_markdown_range( - self.cx.tcx, - dox, - &ori_link.range, - &item.attrs, - ) - .unwrap_or_else(|| item.attr_span(self.cx.tcx)); - - rustc_session::parse::feature_err( - &self.cx.tcx.sess.parse_sess, - sym::intra_doc_pointers, - span, - "linking to associated items of raw pointers is experimental", - ) - .note("rustdoc does not allow disambiguating between `*const` and `*mut`, and pointers are unstable until it does") - .emit(); + self.report_rawptr_assoc_feature_gate(dox, &ori_link, item); } } else { match disambiguator { @@ -1389,6 +1393,20 @@ impl LinkCollector<'_, '_> { } } + fn report_rawptr_assoc_feature_gate(&self, dox: &str, ori_link: &MarkdownLink, item: &Item) { + let span = + super::source_span_for_markdown_range(self.cx.tcx, dox, &ori_link.range, &item.attrs) + .unwrap_or_else(|| item.attr_span(self.cx.tcx)); + rustc_session::parse::feature_err( + &self.cx.tcx.sess.parse_sess, + sym::intra_doc_pointers, + span, + "linking to associated items of raw pointers is experimental", + ) + .note("rustdoc does not allow disambiguating between `*const` and `*mut`, and pointers are unstable until it does") + .emit(); + } + fn resolve_with_disambiguator_cached( &mut self, key: ResolutionInfo, @@ -1399,7 +1417,6 @@ impl LinkCollector<'_, '_> { if let Some(ref cached) = self.visited_links.get(&key) { match cached { Some(cached) => { - self.kind_side_channel.set(cached.side_channel); return Some(cached.res.clone()); } None if cache_resolution_failure => return None, @@ -1416,13 +1433,7 @@ impl LinkCollector<'_, '_> { // Cache only if resolved successfully - don't silence duplicate errors if let Some(res) = res { // Store result for the actual namespace - self.visited_links.insert( - key, - Some(CachedLink { - res: res.clone(), - side_channel: self.kind_side_channel.clone().into_inner(), - }), - ); + self.visited_links.insert(key, Some(CachedLink { res: res.clone() })); Some(res) } else { @@ -1484,7 +1495,7 @@ impl LinkCollector<'_, '_> { let mut candidates = PerNS { macro_ns: self .resolve_macro(path_str, base_node) - .map(|res| (res, extra_fragment.clone())), + .map(|res| (res, extra_fragment.clone().map(UrlFragment::UserWritten))), type_ns: match self.resolve(path_str, TypeNS, base_node, extra_fragment) { Ok(res) => { debug!("got res in TypeNS: {:?}", res); @@ -1516,7 +1527,10 @@ impl LinkCollector<'_, '_> { // Shouldn't happen but who knows? Ok((res, Some(fragment))) } - (fragment, None) | (None, fragment) => Ok((res, fragment)), + (fragment, None) => Ok((res, fragment)), + (None, fragment) => { + Ok((res, fragment.map(UrlFragment::UserWritten))) + } } } } @@ -1553,7 +1567,7 @@ impl LinkCollector<'_, '_> { } Some(MacroNS) => { match self.resolve_macro(path_str, base_node) { - Ok(res) => Some((res, extra_fragment.clone())), + Ok(res) => Some((res, extra_fragment.clone().map(UrlFragment::UserWritten))), Err(mut kind) => { // `resolve_macro` only looks in the macro namespace. Try to give a better error if possible. for ns in [TypeNS, ValueNS] { @@ -2272,20 +2286,13 @@ fn privacy_error(cx: &DocContext<'_>, diag_info: &DiagnosticInfo<'_>, path_str: fn handle_variant( cx: &DocContext<'_>, res: Res, - extra_fragment: &Option<UrlFragment>, -) -> Result<(Res, Option<UrlFragment>), ErrorKind<'static>> { - use rustc_middle::ty::DefIdTree; - - if extra_fragment.is_some() { - // NOTE: `res` can never be a primitive since this function is only called when `tcx.def_kind(res) == DefKind::Variant`. - return Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(res))); - } +) -> Result<(Res, Option<ItemFragment>), ErrorKind<'static>> { cx.tcx .parent(res.def_id(cx.tcx)) .map(|parent| { let parent_def = Res::Def(DefKind::Enum, parent); let variant = cx.tcx.expect_variant_res(res.as_hir_res().unwrap()); - (parent_def, Some(UrlFragment::Variant(variant.ident.name))) + (parent_def, Some(ItemFragment(FragmentKind::Variant, variant.def_id))) }) .ok_or_else(|| ResolutionFailure::NoParentItem.into()) } diff --git a/src/librustdoc/passes/collect_intra_doc_links/early.rs b/src/librustdoc/passes/collect_intra_doc_links/early.rs index 4cebf741e20..31d6ac44a94 100644 --- a/src/librustdoc/passes/collect_intra_doc_links/early.rs +++ b/src/librustdoc/passes/collect_intra_doc_links/early.rs @@ -1,98 +1,136 @@ -use ast::visit; -use rustc_ast as ast; +use crate::clean; +use crate::core::ResolverCaches; +use crate::html::markdown::markdown_links; +use crate::passes::collect_intra_doc_links::preprocess_link; + +use rustc_ast::visit::{self, AssocCtxt, Visitor}; +use rustc_ast::{self as ast, ItemKind}; +use rustc_ast_lowering::ResolverAstLowering; use rustc_hir::def::Namespace::TypeNS; -use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID}; -use rustc_interface::interface; -use rustc_span::Span; +use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; +use rustc_resolve::Resolver; +use rustc_session::config::Externs; +use rustc_span::{Span, DUMMY_SP}; -use std::cell::RefCell; use std::mem; -use std::rc::Rc; - -type Resolver = Rc<RefCell<interface::BoxedResolver>>; -// Letting the resolver escape at the end of the function leads to inconsistencies between the -// crates the TyCtxt sees and the resolver sees (because the resolver could load more crates -// after escaping). Hopefully `IntraLinkCrateLoader` gets all the crates we need ... -crate fn load_intra_link_crates(resolver: Resolver, krate: &ast::Crate) -> Resolver { - let mut loader = IntraLinkCrateLoader { current_mod: CRATE_DEF_ID, resolver }; - // `walk_crate` doesn't visit the crate itself for some reason. + +crate fn early_resolve_intra_doc_links( + resolver: &mut Resolver<'_>, + krate: &ast::Crate, + externs: Externs, +) -> ResolverCaches { + let mut loader = IntraLinkCrateLoader { + resolver, + current_mod: CRATE_DEF_ID, + all_traits: Default::default(), + all_trait_impls: Default::default(), + }; + + // Overridden `visit_item` below doesn't apply to the crate root, + // so we have to visit its attributes and exports separately. loader.load_links_in_attrs(&krate.attrs, krate.span); visit::walk_crate(&mut loader, krate); - loader.resolver + loader.fill_resolver_caches(); + + // FIXME: somehow rustdoc is still missing crates even though we loaded all + // the known necessary crates. Load them all unconditionally until we find a way to fix this. + // DO NOT REMOVE THIS without first testing on the reproducer in + // https://github.com/jyn514/objr/commit/edcee7b8124abf0e4c63873e8422ff81beb11ebb + for (extern_name, _) in externs.iter().filter(|(_, entry)| entry.add_prelude) { + let _ = loader.resolver.resolve_str_path_error( + DUMMY_SP, + extern_name, + TypeNS, + CRATE_DEF_ID.to_def_id(), + ); + } + + ResolverCaches { + all_traits: Some(loader.all_traits), + all_trait_impls: Some(loader.all_trait_impls), + } } -struct IntraLinkCrateLoader { +struct IntraLinkCrateLoader<'r, 'ra> { + resolver: &'r mut Resolver<'ra>, current_mod: LocalDefId, - resolver: Rc<RefCell<interface::BoxedResolver>>, + all_traits: Vec<DefId>, + all_trait_impls: Vec<DefId>, } -impl IntraLinkCrateLoader { - fn load_links_in_attrs(&mut self, attrs: &[ast::Attribute], span: Span) { - use crate::html::markdown::markdown_links; - use crate::passes::collect_intra_doc_links::preprocess_link; +impl IntraLinkCrateLoader<'_, '_> { + fn fill_resolver_caches(&mut self) { + for cnum in self.resolver.cstore().crates_untracked() { + let all_traits = self.resolver.cstore().traits_in_crate_untracked(cnum); + let all_trait_impls = self.resolver.cstore().trait_impls_in_crate_untracked(cnum); - // FIXME: this probably needs to consider inlining - let attrs = crate::clean::Attributes::from_ast(attrs, None); + self.all_traits.extend(all_traits); + self.all_trait_impls.extend(all_trait_impls.into_iter().map(|(def_id, _)| def_id)); + } + } + + fn load_links_in_attrs(&mut self, attrs: &[ast::Attribute], span: Span) { + // FIXME: this needs to consider export inlining. + let attrs = clean::Attributes::from_ast(attrs, None); for (parent_module, doc) in attrs.collapsed_doc_value_by_module_level() { - debug!(?doc); - for link in markdown_links(doc.as_str()) { - debug!(?link.link); + let module_id = parent_module.unwrap_or(self.current_mod.to_def_id()); + + for link in markdown_links(&doc.as_str()) { let path_str = if let Some(Ok(x)) = preprocess_link(&link) { x.path_str } else { continue; }; - self.resolver.borrow_mut().access(|resolver| { - let _ = resolver.resolve_str_path_error( - span, - &path_str, - TypeNS, - parent_module.unwrap_or_else(|| self.current_mod.to_def_id()), - ); - }); + let _ = self.resolver.resolve_str_path_error(span, &path_str, TypeNS, module_id); } } } } -impl visit::Visitor<'_> for IntraLinkCrateLoader { - fn visit_foreign_item(&mut self, item: &ast::ForeignItem) { - self.load_links_in_attrs(&item.attrs, item.span); - visit::walk_foreign_item(self, item) - } - +impl Visitor<'_> for IntraLinkCrateLoader<'_, '_> { fn visit_item(&mut self, item: &ast::Item) { - use rustc_ast_lowering::ResolverAstLowering; - - if let ast::ItemKind::Mod(..) = item.kind { - let new_mod = - self.resolver.borrow_mut().access(|resolver| resolver.local_def_id(item.id)); - let old_mod = mem::replace(&mut self.current_mod, new_mod); + if let ItemKind::Mod(..) = item.kind { + let old_mod = mem::replace(&mut self.current_mod, self.resolver.local_def_id(item.id)); self.load_links_in_attrs(&item.attrs, item.span); visit::walk_item(self, item); self.current_mod = old_mod; } else { + match item.kind { + ItemKind::Trait(..) => { + self.all_traits.push(self.resolver.local_def_id(item.id).to_def_id()); + } + ItemKind::Impl(box ast::Impl { of_trait: Some(..), .. }) => { + self.all_trait_impls.push(self.resolver.local_def_id(item.id).to_def_id()); + } + _ => {} + } self.load_links_in_attrs(&item.attrs, item.span); visit::walk_item(self, item); } } - // NOTE: if doc-comments are ever allowed on function parameters, this will have to implement `visit_param` too. - - fn visit_assoc_item(&mut self, item: &ast::AssocItem, ctxt: visit::AssocCtxt) { + fn visit_assoc_item(&mut self, item: &ast::AssocItem, ctxt: AssocCtxt) { self.load_links_in_attrs(&item.attrs, item.span); visit::walk_assoc_item(self, item, ctxt) } - fn visit_field_def(&mut self, field: &ast::FieldDef) { - self.load_links_in_attrs(&field.attrs, field.span); - visit::walk_field_def(self, field) + fn visit_foreign_item(&mut self, item: &ast::ForeignItem) { + self.load_links_in_attrs(&item.attrs, item.span); + visit::walk_foreign_item(self, item) } fn visit_variant(&mut self, v: &ast::Variant) { self.load_links_in_attrs(&v.attrs, v.span); visit::walk_variant(self, v) } + + fn visit_field_def(&mut self, field: &ast::FieldDef) { + self.load_links_in_attrs(&field.attrs, field.span); + visit::walk_field_def(self, field) + } + + // NOTE: if doc-comments are ever allowed on other nodes (e.g. function parameters), + // then this will have to implement other visitor methods too. } diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index cc1d994dc99..66ac612ea37 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -34,16 +34,18 @@ crate fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> Crate let mut new_items = Vec::new(); - for &cnum in cx.tcx.crates(()).iter() { - for &(did, _) in cx.tcx.all_trait_implementations(cnum).iter() { - inline::build_impl(cx, None, did, None, &mut new_items); + // External trait impls. + cx.with_all_trait_impls(|cx, all_trait_impls| { + let _prof_timer = cx.tcx.sess.prof.generic_activity("build_extern_trait_impls"); + for &impl_def_id in all_trait_impls.iter().skip_while(|def_id| def_id.is_local()) { + inline::build_impl(cx, None, impl_def_id, None, &mut new_items); } - } + }); // Also try to inline primitive impls from other crates. - for &def_id in PrimitiveType::all_impls(cx.tcx).values().flatten() { - if !def_id.is_local() { - cx.tcx.sess.prof.generic_activity("build_primitive_trait_impls").run(|| { + cx.tcx.sess.prof.generic_activity("build_primitive_trait_impls").run(|| { + for &def_id in PrimitiveType::all_impls(cx.tcx).values().flatten() { + if !def_id.is_local() { inline::build_impl(cx, None, def_id, None, &mut new_items); // FIXME(eddyb) is this `doc(hidden)` check needed? @@ -51,9 +53,9 @@ crate fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> Crate let impls = get_auto_trait_and_blanket_impls(cx, def_id); new_items.extend(impls.filter(|i| cx.inlined.insert(i.def_id))); } - }); + } } - } + }); let mut cleaner = BadImplStripper { prims, items: crate_items }; let mut type_did_to_deref_target: FxHashMap<DefId, &Type> = FxHashMap::default(); @@ -126,36 +128,33 @@ crate fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> Crate } }); - // `tcx.crates(())` doesn't include the local crate, and `tcx.all_trait_implementations` - // doesn't work with it anyway, so pull them from the HIR map instead - let mut extra_attrs = Vec::new(); - for trait_did in cx.tcx.all_traits() { - for &impl_did in cx.tcx.hir().trait_impls(trait_did) { - let impl_did = impl_did.to_def_id(); - cx.tcx.sess.prof.generic_activity("build_local_trait_impl").run(|| { - let mut parent = cx.tcx.parent(impl_did); - while let Some(did) = parent { - extra_attrs.extend( - cx.tcx - .get_attrs(did) - .iter() - .filter(|attr| attr.has_name(sym::doc)) - .filter(|attr| { - if let Some([attr]) = attr.meta_item_list().as_deref() { - attr.has_name(sym::cfg) - } else { - false - } - }) - .cloned(), - ); - parent = cx.tcx.parent(did); - } - inline::build_impl(cx, None, impl_did, Some(&extra_attrs), &mut new_items); - extra_attrs.clear(); - }); + // Local trait impls. + cx.with_all_trait_impls(|cx, all_trait_impls| { + let _prof_timer = cx.tcx.sess.prof.generic_activity("build_local_trait_impls"); + let mut attr_buf = Vec::new(); + for &impl_def_id in all_trait_impls.iter().take_while(|def_id| def_id.is_local()) { + let mut parent = cx.tcx.parent(impl_def_id); + while let Some(did) = parent { + attr_buf.extend( + cx.tcx + .get_attrs(did) + .iter() + .filter(|attr| attr.has_name(sym::doc)) + .filter(|attr| { + if let Some([attr]) = attr.meta_item_list().as_deref() { + attr.has_name(sym::cfg) + } else { + false + } + }) + .cloned(), + ); + parent = cx.tcx.parent(did); + } + inline::build_impl(cx, None, impl_def_id, Some(&attr_buf), &mut new_items); + attr_buf.clear(); } - } + }); if let ModuleItem(Module { items, .. }) = &mut *krate.module.kind { items.extend(synth_impls); diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs index 675443b48a2..7b07974ae01 100644 --- a/src/librustdoc/passes/stripper.rs +++ b/src/librustdoc/passes/stripper.rs @@ -43,11 +43,10 @@ impl<'a> DocFolder for Stripper<'a> { | clean::TraitAliasItem(..) | clean::MacroItem(..) | clean::ForeignTypeItem => { - if i.def_id.is_local() { - if !self.access_levels.is_exported(i.def_id.expect_def_id()) { - debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name); - return None; - } + if i.def_id.is_local() && !self.access_levels.is_exported(i.def_id.expect_def_id()) + { + debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name); + return None; } } diff --git a/src/librustdoc/html/templates/STYLE.md b/src/librustdoc/templates/STYLE.md index fff65e3b5ff..fff65e3b5ff 100644 --- a/src/librustdoc/html/templates/STYLE.md +++ b/src/librustdoc/templates/STYLE.md diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/templates/page.html index 00b46b1ba91..02808754b53 100644 --- a/src/librustdoc/html/templates/page.html +++ b/src/librustdoc/templates/page.html @@ -7,20 +7,20 @@ <meta name="description" content="{{page.description}}"> {#- -#} <meta name="keywords" content="{{page.keywords}}"> {#- -#} <title>{{page.title}}</title> {#- -#} - <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path | safe}}SourceSerif4-Regular.ttf.woff2"> {#- -#} - <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path | safe}}FiraSans-Regular.woff2"> {#- -#} - <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path | safe}}FiraSans-Medium.woff2"> {#- -#} - <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path | safe}}SourceCodePro-Regular.ttf.woff2"> {#- -#} - <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path | safe}}SourceSerif4-Bold.ttf.woff2"> {#- -#} - <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path | safe}}SourceCodePro-Semibold.ttf.woff2"> {#- -#} + <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}SourceSerif4-Regular.ttf.woff2"> {#- -#} + <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}FiraSans-Regular.woff2"> {#- -#} + <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}FiraSans-Medium.woff2"> {#- -#} + <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}SourceCodePro-Regular.ttf.woff2"> {#- -#} + <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}SourceSerif4-Bold.ttf.woff2"> {#- -#} + <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}SourceCodePro-Semibold.ttf.woff2"> {#- -#} <link rel="stylesheet" type="text/css" {# -#} - href="{{static_root_path | safe}}normalize{{page.resource_suffix}}.css"> {#- -#} + href="{{static_root_path|safe}}normalize{{page.resource_suffix}}.css"> {#- -#} <link rel="stylesheet" type="text/css" {# -#} - href="{{static_root_path | safe}}rustdoc{{page.resource_suffix}}.css" {# -#} + href="{{static_root_path|safe}}rustdoc{{page.resource_suffix}}.css" {# -#} id="mainThemeStyle"> {#- -#} {%- for theme in themes -%} <link rel="stylesheet" type="text/css" {# -#} - href="{{static_root_path | safe}}{{theme}}{{page.resource_suffix}}.css" {# -#} + href="{{static_root_path|safe}}{{theme}}{{page.resource_suffix}}.css" {# -#} {%- if theme == "light" -%} id="themeStyle" {%- else -%} @@ -29,41 +29,41 @@ > {%- endfor -%} <script id="default-settings" {# -#} - {% for k, v in layout.default_settings %} + {% for (k, v) in layout.default_settings %} data-{{k}}="{{v}}" {%- endfor -%} ></script> {#- -#} - <script src="{{static_root_path | safe}}storage{{page.resource_suffix}}.js"></script> {#- -#} - <script src="{{page.root_path | safe}}crates{{page.resource_suffix}}.js"></script> {#- -#} - <script defer src="{{static_root_path | safe}}main{{page.resource_suffix}}.js"></script> {#- -#} + <script src="{{static_root_path|safe}}storage{{page.resource_suffix}}.js"></script> {#- -#} + <script src="{{page.root_path|safe}}crates{{page.resource_suffix}}.js"></script> {#- -#} + <script defer src="{{static_root_path|safe}}main{{page.resource_suffix}}.js"></script> {#- -#} {%- for script in page.static_extra_scripts -%} - <script defer src="{{static_root_path | safe}}{{script}}.js"></script> {#- -#} + <script defer src="{{static_root_path|safe}}{{script}}.js"></script> {#- -#} {% endfor %} {%- if layout.scrape_examples_extension -%} - <script defer src="{{page.root_path | safe}}scrape-examples{{page.resource_suffix}}.js"></script> {#- -#} + <script defer src="{{page.root_path|safe}}scrape-examples{{page.resource_suffix}}.js"></script> {#- -#} {%- endif -%} {%- for script in page.extra_scripts -%} - <script defer src="{{page.root_path | safe}}{{script}}.js"></script> {#- -#} + <script defer src="{{page.root_path|safe}}{{script}}.js"></script> {#- -#} {% endfor %} <noscript> {#- -#} <link rel="stylesheet" {# -#} - href="{{static_root_path | safe}}noscript{{page.resource_suffix}}.css"> {#- -#} + href="{{static_root_path|safe}}noscript{{page.resource_suffix}}.css"> {#- -#} </noscript> {#- -#} - {%- if layout.css_file_extension -%} + {%- if layout.css_file_extension.is_some() -%} <link rel="stylesheet" type="text/css" {# -#} - href="{{static_root_path | safe}}theme{{page.resource_suffix}}.css"> {#- -#} + href="{{static_root_path|safe}}theme{{page.resource_suffix}}.css"> {#- -#} {%- endif -%} - {%- if layout.favicon -%} + {%- if !layout.favicon.is_empty() -%} <link rel="shortcut icon" href="{{layout.favicon}}"> {#- -#} {%- else -%} <link rel="alternate icon" type="image/png" {# -#} - href="{{static_root_path | safe}}favicon-16x16{{page.resource_suffix}}.png"> {#- -#} + href="{{static_root_path|safe}}favicon-16x16{{page.resource_suffix}}.png"> {#- -#} <link rel="alternate icon" type="image/png" {# -#} - href="{{static_root_path | safe}}favicon-32x32{{page.resource_suffix}}.png"> {#- -#} + href="{{static_root_path|safe}}favicon-32x32{{page.resource_suffix}}.png"> {#- -#} <link rel="icon" type="image/svg+xml" {# -#} - href="{{static_root_path | safe}}favicon{{page.resource_suffix}}.svg"> {#- -#} + href="{{static_root_path|safe}}favicon{{page.resource_suffix}}.svg"> {#- -#} {%- endif -%} - {{- layout.external_html.in_header | safe -}} + {{- layout.external_html.in_header|safe -}} </head> {#- -#} <body class="rustdoc {{page.css_class}}"> {#- -#} <!--[if lte IE 11]> {#- -#} @@ -71,45 +71,41 @@ This old browser is unsupported and will most likely display funky things. {#- -#} </div> {#- -#} <![endif]--> {#- -#} - {{- layout.external_html.before_content | safe -}} + {{- layout.external_html.before_content|safe -}} <nav class="sidebar"> {#- -#} <div class="sidebar-menu" role="button">☰</div> {#- -#} - <a class="sidebar-logo" href="{{page.root_path | safe}}{{krate_with_trailing_slash | safe}}index.html"> {#- -#} + <a class="sidebar-logo" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {#- -#} <div class="logo-container"> {#- -#} - {%- if layout.logo -%} - <img src="{{layout.logo}}" alt="logo"> {#- -#} - {%- else -%} - <img class="rust-logo" src="{{static_root_path | safe}}rust-logo{{page.resource_suffix}}.png" alt="logo"> {#- -#} - {%- endif -%} + {%- if !layout.logo.is_empty() %} + <img src="{{layout.logo}}" alt="logo"> {#- -#} + {%- else -%} + <img class="rust-logo" src="{{static_root_path|safe}}rust-logo{{page.resource_suffix}}.svg" alt="logo"> {#- -#} + {%- endif -%} </div> </a> {#- -#} - {{- sidebar | safe -}} + {{- sidebar|safe -}} </nav> {#- -#} <main> {#- -#} <div class="width-limiter"> {#- -#} <div class="sub-container"> {#- -#} - <a class="sub-logo-container" href="{{page.root_path | safe}}{{krate_with_trailing_slash | safe}}index.html"> {#- -#} - {%- if layout.logo -%} - <img src="{{layout.logo}}" alt="logo"> + <a class="sub-logo-container" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {#- -#} + {%- if !layout.logo.is_empty() %} + <img src="{{layout.logo}}" alt="logo"> {#- -#} {%- else -%} - <img class="rust-logo" src="{{static_root_path | safe}}rust-logo{{page.resource_suffix}}.png" alt="logo"> + <img class="rust-logo" src="{{static_root_path|safe}}rust-logo{{page.resource_suffix}}.svg" alt="logo"> {#- -#} {%- endif -%} </a> {#- -#} <nav class="sub"> {#- -#} <div class="theme-picker"> {#- -#} <button id="theme-picker" aria-label="Pick another theme!" aria-haspopup="menu" title="themes"> {#- -#} <img width="18" height="18" alt="Pick another theme!" {# -#} - src="{{static_root_path | safe}}brush{{page.resource_suffix}}.svg"> {#- -#} + src="{{static_root_path|safe}}brush{{page.resource_suffix}}.svg"> {#- -#} </button> {#- -#} <div id="theme-choices" role="menu"></div> {#- -#} </div> {#- -#} <form class="search-form"> {#- -#} <div class="search-container"> {#- -#} - <div>{%- if layout.generate_search_filter -%} - <select id="crate-search"> {#- -#} - <option value="All crates">All crates</option> {#- -#} - </select> {#- -#} - {%- endif -%} + <div> <input {# -#} class="search-input" {# -#} name="search" {# -#} @@ -119,23 +115,23 @@ type="search"> {#- -#} </div> {#- -#} <button type="button" id="help-button" title="help">?</button> {#- -#} - <a id="settings-menu" href="{{page.root_path | safe}}settings.html" title="settings"> {#- -#} + <a id="settings-menu" href="{{page.root_path|safe}}settings.html" title="settings"> {#- -#} <img width="18" height="18" alt="Change settings" {# -#} - src="{{static_root_path | safe}}wheel{{page.resource_suffix}}.svg"> {#- -#} + src="{{static_root_path|safe}}wheel{{page.resource_suffix}}.svg"> {#- -#} </a> {#- -#} </div> {#- -#} </form> {#- -#} </nav> {#- -#} </div> {#- -#} - <section id="main-content" class="content">{{- content | safe -}}</section> {#- -#} + <section id="main-content" class="content">{{- content|safe -}}</section> {#- -#} <section id="search" class="content hidden"></section> {#- -#} </div> {#- -#} </main> {#- -#} - {{- layout.external_html.after_content | safe -}} + {{- layout.external_html.after_content|safe -}} <div id="rustdoc-vars" {# -#} - data-root-path="{{page.root_path | safe}}" {# -#} + data-root-path="{{page.root_path|safe}}" {# -#} data-current-crate="{{layout.krate}}" {# -#} - data-themes="{{themes | join(sep=",") }}" {# -#} + data-themes="{{themes|join(",") }}" {# -#} data-resource-suffix="{{page.resource_suffix}}" {# -#} data-rustdoc-version="{{rustdoc_version}}" {# -#} > {#- -#} diff --git a/src/librustdoc/templates/print_item.html b/src/librustdoc/templates/print_item.html new file mode 100644 index 00000000000..459b01a9960 --- /dev/null +++ b/src/librustdoc/templates/print_item.html @@ -0,0 +1,30 @@ +<div class="main-heading"> + <h1 class="fqn"> {#- -#} + <span class="in-band"> {#- -#} + {{-typ-}} + {#- The breadcrumbs of the item path, like std::string -#} + {%- for component in path_components -%} + <a href="{{component.path|safe}}index.html">{{component.name}}</a>::<wbr> + {%- endfor -%} + <a class="{{item_type}}" href="#">{{name}}</a> {#- -#} + <button id="copy-path" onclick="copy_path(this)" title="Copy item path to clipboard"> {#- -#} + <img src="{{static_root_path|safe}}clipboard{{page.resource_suffix}}.svg" {# -#} + width="19" height="18" {# -#} + alt="Copy item path"> {#- -#} + </button> {#- -#} + </span> {#- -#} + </h1> {#- -#} + <span class="out-of-band"> {#- -#} + {% if !stability_since_raw.is_empty() %} + {{- stability_since_raw|safe -}} · {# -#} + {% endif %} + {%- match src_href -%} + {%- when Some with (href) -%} + <a class="srclink" href="{{href|safe}}" title="goto source code">source</a> · {# -#} + {%- else -%} + {%- endmatch -%} + <a id="toggle-all-docs" href="javascript:void(0)" title="collapse all docs"> {#- -#} + [<span class="inner">−</span>] {#- -#} + </a> {#- -#} + </span> {#- -#} +</div> diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 8e29cb16a40..90cb5d586c2 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -5,7 +5,6 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::definitions::DefPathData; use rustc_hir::Node; use rustc_hir::CRATE_HIR_ID; use rustc_middle::middle::privacy::AccessLevel; @@ -43,12 +42,9 @@ impl Module<'_> { } // FIXME: Should this be replaced with tcx.def_path_str? -fn def_id_to_path(tcx: TyCtxt<'_>, did: DefId) -> Vec<String> { - let crate_name = tcx.crate_name(did.krate).to_string(); - let relative = tcx.def_path(did).data.into_iter().filter_map(|elem| { - // Filter out extern blocks - (elem.data != DefPathData::ForeignMod).then(|| elem.data.to_string()) - }); +fn def_id_to_path(tcx: TyCtxt<'_>, did: DefId) -> Vec<Symbol> { + let crate_name = tcx.crate_name(did.krate); + let relative = tcx.def_path(did).data.into_iter().filter_map(|elem| elem.data.get_opt_name()); std::iter::once(crate_name).chain(relative).collect() } @@ -71,7 +67,7 @@ crate struct RustdocVisitor<'a, 'tcx> { inlining: bool, /// Are the current module and all of its parents public? inside_public_path: bool, - exact_paths: FxHashMap<DefId, Vec<String>>, + exact_paths: FxHashMap<DefId, Vec<Symbol>>, } impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { @@ -112,7 +108,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { // is declared but also a reexport of itself producing two exports of the same // macro in the same module. let mut inserted = FxHashSet::default(); - for export in self.cx.tcx.module_exports(CRATE_DEF_ID).unwrap_or(&[]) { + for export in self.cx.tcx.module_reexports(CRATE_DEF_ID).unwrap_or(&[]) { if let Res::Def(DefKind::Macro(_), def_id) = export.res { if let Some(local_def_id) = def_id.as_local() { if self.cx.tcx.has_attr(def_id, sym::macro_export) { diff --git a/src/librustdoc/visit_lib.rs b/src/librustdoc/visit_lib.rs index ce94f06d574..5bcec779bc0 100644 --- a/src/librustdoc/visit_lib.rs +++ b/src/librustdoc/visit_lib.rs @@ -53,7 +53,7 @@ impl<'a, 'tcx> LibEmbargoVisitor<'a, 'tcx> { return; } - for item in self.tcx.item_children(def_id).iter() { + for item in self.tcx.module_children(def_id).iter() { if let Some(def_id) = item.res.opt_def_id() { if self.tcx.def_key(def_id).parent.map_or(false, |d| d == def_id.index) || item.vis.is_public() diff --git a/src/llvm-project b/src/llvm-project -Subproject 6b3dbcc81a470e5da84576d63fcfc19e3b1154c +Subproject 2abffbf977a9e8c6ca4174a08fe5c4d7781f0aa diff --git a/src/test/incremental/hashes/trait_defs.rs b/src/test/incremental/hashes/trait_defs.rs index b72ec404f67..14d6fc87198 100644 --- a/src/test/incremental/hashes/trait_defs.rs +++ b/src/test/incremental/hashes/trait_defs.rs @@ -400,7 +400,7 @@ trait TraitAddUnsafeModifier { #[cfg(not(any(cfail1,cfail4)))] #[rustc_clean(cfg="cfail2")] #[rustc_clean(cfg="cfail3")] -#[rustc_clean(cfg="cfail5")] +#[rustc_clean(except="hir_owner", cfg="cfail5")] #[rustc_clean(cfg="cfail6")] trait TraitAddUnsafeModifier { #[rustc_clean(except="hir_owner,fn_sig", cfg="cfail2")] @@ -425,7 +425,7 @@ trait TraitAddExternModifier { #[cfg(not(any(cfail1,cfail4)))] #[rustc_clean(cfg="cfail2")] #[rustc_clean(cfg="cfail3")] -#[rustc_clean(cfg="cfail5")] +#[rustc_clean(except="hir_owner", cfg="cfail5")] #[rustc_clean(cfg="cfail6")] trait TraitAddExternModifier { #[rustc_clean(except="hir_owner,fn_sig", cfg="cfail2")] diff --git a/src/test/run-make-fulldeps/coverage-reports/Makefile b/src/test/run-make-fulldeps/coverage-reports/Makefile index 9122e0406c2..094d6b3ebf5 100644 --- a/src/test/run-make-fulldeps/coverage-reports/Makefile +++ b/src/test/run-make-fulldeps/coverage-reports/Makefile @@ -64,7 +64,7 @@ endif # if and when we allow `llvm-cov` to produce results for multiple files. Note, the path separators # appear to be normalized to `/` in those files, thankfully.) LLVM_COV_IGNORE_FILES=\ - --ignore-filename-regex='(uses_crate.rs|uses_inline_crate.rs)' + --ignore-filename-regex='(uses_crate.rs|uses_inline_crate.rs|unused_mod.rs)' all: $(patsubst $(SOURCEDIR)/lib/%.rs,%,$(wildcard $(SOURCEDIR)/lib/*.rs)) $(patsubst $(SOURCEDIR)/%.rs,%,$(wildcard $(SOURCEDIR)/*.rs)) diff --git a/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.unused_mod.txt b/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.unused_mod.txt new file mode 100644 index 00000000000..82d6fccc271 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.unused_mod.txt @@ -0,0 +1,4 @@ + 1| 0|pub fn never_called_function() { + 2| 0| println!("I am never called"); + 3| 0|} + diff --git a/src/test/run-make-fulldeps/coverage/lib/unused_mod_helper.rs b/src/test/run-make-fulldeps/coverage/lib/unused_mod_helper.rs new file mode 100644 index 00000000000..ae1cc1531ed --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/lib/unused_mod_helper.rs @@ -0,0 +1,3 @@ +pub fn never_called_function() { + println!("I am never called"); +} diff --git a/src/test/run-make-fulldeps/coverage/unused_mod.rs b/src/test/run-make-fulldeps/coverage/unused_mod.rs new file mode 100644 index 00000000000..679b4e53188 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/unused_mod.rs @@ -0,0 +1,6 @@ +#[path = "lib/unused_mod_helper.rs"] +mod unused_module; + +fn main() { + println!("hello world!"); +} diff --git a/src/test/rustdoc-gui/anchors.goml b/src/test/rustdoc-gui/anchors.goml index ca60be72cea..e6758287d8c 100644 --- a/src/test/rustdoc-gui/anchors.goml +++ b/src/test/rustdoc-gui/anchors.goml @@ -13,8 +13,10 @@ reload: assert-css: ("#toggle-all-docs", {"color": "rgb(0, 0, 0)"}) assert-css: (".fqn .in-band a:nth-of-type(1)", {"color": "rgb(0, 0, 0)"}) assert-css: (".fqn .in-band a:nth-of-type(2)", {"color": "rgb(173, 55, 138)"}) -assert-css: (".srclink", {"color": "rgb(0, 0, 0)"}) -assert-css: (".srclink", {"color": "rgb(0, 0, 0)"}) +assert-css: (".srclink", {"color": "rgb(56, 115, 173)"}) + +move-cursor-to: ".main-heading .srclink" +assert-css: (".srclink", {"text-decoration": "underline solid rgb(56, 115, 173)"}) assert-css: ("#top-doc-prose-title", {"color": "rgb(0, 0, 0)"}) diff --git a/src/test/rustdoc-gui/escape-key.goml b/src/test/rustdoc-gui/escape-key.goml index b65c398405c..712920b16a9 100644 --- a/src/test/rustdoc-gui/escape-key.goml +++ b/src/test/rustdoc-gui/escape-key.goml @@ -1,7 +1,7 @@ goto: file://|DOC_PATH|/test_docs/index.html // First, we check that the search results are hidden when the Escape key is pressed. write: (".search-input", "test") -wait-for: "#search > h1" // The search element is empty before the first search +wait-for: "#search h1" // The search element is empty before the first search assert-attribute: ("#search", {"class": "content"}) assert-attribute: ("#main-content", {"class": "content hidden"}) press-key: "Escape" diff --git a/src/test/rustdoc-gui/headings.goml b/src/test/rustdoc-gui/headings.goml index 87c512468e0..9db75c59d94 100644 --- a/src/test/rustdoc-gui/headings.goml +++ b/src/test/rustdoc-gui/headings.goml @@ -15,7 +15,7 @@ goto: file://|DOC_PATH|/test_docs/struct.HeavilyDocumentedStruct.html assert-css: ("h1.fqn", {"font-size": "24px"}) -assert-css: ("h1.fqn", {"border-bottom-width": "1px"}) +assert-css: (".main-heading", {"border-bottom-width": "1px"}) assert-css: ("h2#top-doc-prose-title", {"font-size": "20.8px"}) assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"}) @@ -55,7 +55,7 @@ assert-css: ("h6#sub-sub-heading-for-struct-impl-item-doc", {"font-size": "15.2p goto: file://|DOC_PATH|/test_docs/enum.HeavilyDocumentedEnum.html assert-css: ("h1.fqn", {"font-size": "24px"}) -assert-css: ("h1.fqn", {"border-bottom-width": "1px"}) +assert-css: (".main-heading", {"border-bottom-width": "1px"}) assert-css: ("h2#top-doc-prose-title", {"font-size": "20.8px"}) assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"}) @@ -115,7 +115,7 @@ assert-css: (".sidebar .others h3", {"border-bottom-width": "1px"}, ALL) goto: file://|DOC_PATH|/test_docs/union.HeavilyDocumentedUnion.html assert-css: ("h1.fqn", {"font-size": "24px"}) -assert-css: ("h1.fqn", {"border-bottom-width": "1px"}) +assert-css: (".main-heading", {"border-bottom-width": "1px"}) assert-css: ("h2#top-doc-prose-title", {"font-size": "20.8px"}) assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"}) @@ -148,7 +148,7 @@ assert-css: ("h6#sub-heading-for-union-impl-item-doc", {"border-bottom-width": " goto: file://|DOC_PATH|/test_docs/macro.heavily_documented_macro.html assert-css: ("h1.fqn", {"font-size": "24px"}) -assert-css: ("h1.fqn", {"border-bottom-width": "1px"}) +assert-css: (".main-heading", {"border-bottom-width": "1px"}) assert-css: ("h2#top-doc-prose-title", {"font-size": "20.8px"}) assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"}) diff --git a/src/test/rustdoc-gui/rust-logo.goml b/src/test/rustdoc-gui/rust-logo.goml new file mode 100644 index 00000000000..4a9dcf73506 --- /dev/null +++ b/src/test/rustdoc-gui/rust-logo.goml @@ -0,0 +1,78 @@ +// This test ensures that the correct style is applied to the rust logo in the sidebar. +goto: file://|DOC_PATH|/test_docs/index.html + +// First we start with the dark theme. +local-storage: { + "rustdoc-theme": "dark", + "rustdoc-preferred-dark-theme": "dark", + "rustdoc-use-system-theme": "false", +} +reload: + +assert-css: ( + ".rust-logo", + {"filter": "drop-shadow(rgb(255, 255, 255) 1px 0px 0px) drop-shadow(rgb(255, 255, 255) 0px 1px 0px) drop-shadow(rgb(255, 255, 255) -1px 0px 0px) drop-shadow(rgb(255, 255, 255) 0px -1px 0px)"}, +) + +// In the source view page now. +goto: file://|DOC_PATH|/src/test_docs/lib.rs.html + +local-storage: { + "rustdoc-theme": "dark", + "rustdoc-preferred-dark-theme": "dark", + "rustdoc-use-system-theme": "false", +} +reload: + +assert-css: ( + ".rust-logo", + {"filter": "drop-shadow(rgb(255, 255, 255) 1px 0px 0px) drop-shadow(rgb(255, 255, 255) 0px 1px 0px) drop-shadow(rgb(255, 255, 255) -1px 0px 0px) drop-shadow(rgb(255, 255, 255) 0px -1px 0px)"}, +) + +// Then with the ayu theme. +local-storage: { + "rustdoc-theme": "ayu", + "rustdoc-preferred-dark-theme": "ayu", + "rustdoc-use-system-theme": "false", +} +reload: + +assert-css: ( + ".rust-logo", + {"filter": "drop-shadow(rgb(255, 255, 255) 1px 0px 0px) drop-shadow(rgb(255, 255, 255) 0px 1px 0px) drop-shadow(rgb(255, 255, 255) -1px 0px 0px) drop-shadow(rgb(255, 255, 255) 0px -1px 0px)"}, +) + +// In the source view page now. +goto: file://|DOC_PATH|/src/test_docs/lib.rs.html + +local-storage: { + "rustdoc-theme": "ayu", + "rustdoc-preferred-dark-theme": "ayu", + "rustdoc-use-system-theme": "false", +} +reload: + +assert-css: ( + ".rust-logo", + {"filter": "drop-shadow(rgb(255, 255, 255) 1px 0px 0px) drop-shadow(rgb(255, 255, 255) 0px 1px 0px) drop-shadow(rgb(255, 255, 255) -1px 0px 0px) drop-shadow(rgb(255, 255, 255) 0px -1px 0px)"}, +) + +// And finally with the light theme. +local-storage: {"rustdoc-theme": "light", "rustdoc-use-system-theme": "false"} +reload: + +assert-css: ( + ".rust-logo", + {"filter": "none"}, +) + +// In the source view page now. +goto: file://|DOC_PATH|/src/test_docs/lib.rs.html + +local-storage: {"rustdoc-theme": "light", "rustdoc-use-system-theme": "false"} +reload: + +assert-css: ( + ".rust-logo", + {"filter": "none"}, +) diff --git a/src/test/rustdoc-gui/search-filter.goml b/src/test/rustdoc-gui/search-filter.goml index 7a8f8ca5311..e5cdf3ea7a1 100644 --- a/src/test/rustdoc-gui/search-filter.goml +++ b/src/test/rustdoc-gui/search-filter.goml @@ -5,14 +5,12 @@ write: (".search-input", "test") wait-for: "#titles" assert-text: ("#results .externcrate", "test_docs") -goto: file://|DOC_PATH|/test_docs/index.html +wait-for: "#crate-search" // We now want to change the crate filter. click: "#crate-search" // We select "lib2" option then press enter to change the filter. press-key: "ArrowDown" press-key: "Enter" -// We now make the search again. -write: (".search-input", "test") // Waiting for the search results to appear... wait-for: "#titles" // We check that there is no more "test_docs" appearing. diff --git a/src/test/rustdoc-gui/toggle-docs-mobile.goml b/src/test/rustdoc-gui/toggle-docs-mobile.goml index b370dd012fa..6e0ad8e0fa7 100644 --- a/src/test/rustdoc-gui/toggle-docs-mobile.goml +++ b/src/test/rustdoc-gui/toggle-docs-mobile.goml @@ -1,12 +1,12 @@ goto: file://|DOC_PATH|/test_docs/struct.Foo.html size: (433, 600) assert-attribute: (".top-doc", {"open": ""}) -click: (4, 280) // This is the position of the top doc comment toggle +click: (4, 240) // This is the position of the top doc comment toggle assert-attribute-false: (".top-doc", {"open": ""}) -click: (4, 280) +click: (4, 240) assert-attribute: (".top-doc", {"open": ""}) // To ensure that the toggle isn't over the text, we check that the toggle isn't clicked. -click: (3, 280) +click: (3, 240) assert-attribute: (".top-doc", {"open": ""}) // Assert the position of the toggle on the top doc block. diff --git a/src/test/rustdoc-gui/toggle-docs.goml b/src/test/rustdoc-gui/toggle-docs.goml index eaa4c554d5a..e11aae21e3f 100644 --- a/src/test/rustdoc-gui/toggle-docs.goml +++ b/src/test/rustdoc-gui/toggle-docs.goml @@ -1,10 +1,13 @@ goto: file://|DOC_PATH|/test_docs/index.html assert-attribute: ("#main-content > details.top-doc", {"open": ""}) +assert-text: ("#toggle-all-docs", "[−]") click: "#toggle-all-docs" wait-for: 1000 // This is now collapsed so there shouldn't be the "open" attribute on details. assert-attribute-false: ("#main-content > details.top-doc", {"open": ""}) +assert-text: ("#toggle-all-docs", "[+]") click: "#toggle-all-docs" wait-for: 1000 // Not collapsed anymore so the "open" attribute should be back. assert-attribute: ("#main-content > details.top-doc", {"open": ""}) +assert-text: ("#toggle-all-docs", "[−]") diff --git a/src/test/rustdoc/decl_macro.rs b/src/test/rustdoc/decl_macro.rs index fe19dadbe02..94ade31b5e5 100644 --- a/src/test/rustdoc/decl_macro.rs +++ b/src/test/rustdoc/decl_macro.rs @@ -9,7 +9,7 @@ pub macro my_macro() { } -// @has decl_macro/macro.my_macro_2.html //pre 'pub macro my_macro_2($($tok : tt) *) {' +// @has decl_macro/macro.my_macro_2.html //pre 'pub macro my_macro_2($($tok:tt)*) {' // @has - //pre '...' // @has - //pre '}' pub macro my_macro_2($($tok:tt)*) { @@ -18,8 +18,8 @@ pub macro my_macro_2($($tok:tt)*) { // @has decl_macro/macro.my_macro_multi.html //pre 'pub macro my_macro_multi {' // @has - //pre '(_) => { ... },' -// @has - //pre '($foo : ident.$bar : expr) => { ... },' -// @has - //pre '($($foo : literal), +) => { ... },' +// @has - //pre '($foo:ident . $bar:expr) => { ... },' +// @has - //pre '($($foo:literal),+) => { ... },' // @has - //pre '}' pub macro my_macro_multi { (_) => { @@ -33,7 +33,7 @@ pub macro my_macro_multi { } } -// @has decl_macro/macro.by_example_single.html //pre 'pub macro by_example_single($foo : expr) {' +// @has decl_macro/macro.by_example_single.html //pre 'pub macro by_example_single($foo:expr) {' // @has - //pre '...' // @has - //pre '}' pub macro by_example_single { @@ -42,12 +42,12 @@ pub macro by_example_single { mod a { mod b { - // @has decl_macro/a/b/macro.by_example_vis.html //pre 'pub(super) macro by_example_vis($foo : expr) {' + // @has decl_macro/a/b/macro.by_example_vis.html //pre 'pub(super) macro by_example_vis($foo:expr) {' pub(in super) macro by_example_vis { ($foo:expr) => {} } mod c { - // @has decl_macro/a/b/c/macro.by_example_vis_named.html //pre 'pub(in a) macro by_example_vis_named($foo : expr) {' + // @has decl_macro/a/b/c/macro.by_example_vis_named.html //pre 'pub(in a) macro by_example_vis_named($foo:expr) {' pub(in a) macro by_example_vis_named { ($foo:expr) => {} } diff --git a/src/test/rustdoc/ensure-src-link.rs b/src/test/rustdoc/ensure-src-link.rs index 6189acb7254..d32d3fc581f 100644 --- a/src/test/rustdoc/ensure-src-link.rs +++ b/src/test/rustdoc/ensure-src-link.rs @@ -2,5 +2,5 @@ // This test ensures that the [src] link is present on traits items. -// @has foo/trait.Iterator.html '//div[@id="method.zip"]//a[@class="srclink"]' "[src]" +// @has foo/trait.Iterator.html '//div[@id="method.zip"]//a[@class="srclink"]' "source" pub use std::iter::Iterator; diff --git a/src/test/rustdoc/external-macro-src.rs b/src/test/rustdoc/external-macro-src.rs index 6a7dbb004a3..359551ab78d 100644 --- a/src/test/rustdoc/external-macro-src.rs +++ b/src/test/rustdoc/external-macro-src.rs @@ -5,8 +5,8 @@ #[macro_use] extern crate external_macro_src; -// @has foo/index.html '//a[@href="../src/foo/external-macro-src.rs.html#3-12"]' '[src]' +// @has foo/index.html '//a[@href="../src/foo/external-macro-src.rs.html#3-12"]' 'source' // @has foo/struct.Foo.html -// @has - '//a[@href="../src/foo/external-macro-src.rs.html#12"]' '[src]' +// @has - '//a[@href="../src/foo/external-macro-src.rs.html#12"]' 'source' make_foo!(); diff --git a/src/test/rustdoc/issue-16265-1.rs b/src/test/rustdoc/issue-16265-1.rs index 653fd4c5e34..ec007e36b72 100644 --- a/src/test/rustdoc/issue-16265-1.rs +++ b/src/test/rustdoc/issue-16265-1.rs @@ -1,8 +1,10 @@ pub struct Foo; -// @has issue_16265_1/traits/index.html '[src]' +// @has issue_16265_1/traits/index.html 'source' pub mod traits { impl PartialEq for super::Foo { - fn eq(&self, _: &super::Foo) -> bool { true } + fn eq(&self, _: &super::Foo) -> bool { + true + } } } diff --git a/src/test/rustdoc/issue-16265-2.rs b/src/test/rustdoc/issue-16265-2.rs index 00453a36333..d5cd18d9daf 100644 --- a/src/test/rustdoc/issue-16265-2.rs +++ b/src/test/rustdoc/issue-16265-2.rs @@ -1,4 +1,4 @@ -// @has issue_16265_2/index.html '[src]' +// @has issue_16265_2/index.html 'source' trait Y {} -impl Y for Option<u32>{} +impl Y for Option<u32> {} diff --git a/src/test/rustdoc/issue-26606.rs b/src/test/rustdoc/issue-26606.rs index bd6f38e9123..d5cb2c710cd 100644 --- a/src/test/rustdoc/issue-26606.rs +++ b/src/test/rustdoc/issue-26606.rs @@ -7,5 +7,5 @@ extern crate issue_26606_macro; // @has issue_26606/constant.FOO.html -// @has - '//a[@href="../src/issue_26606/issue-26606.rs.html#11"]' '[src]' +// @has - '//a[@href="../src/issue_26606/issue-26606.rs.html#11"]' 'source' make_item!(FOO); diff --git a/src/test/rustdoc/issue-88600.rs b/src/test/rustdoc/issue-88600.rs index 3761805b48b..fc63ed343bd 100644 --- a/src/test/rustdoc/issue-88600.rs +++ b/src/test/rustdoc/issue-88600.rs @@ -18,17 +18,18 @@ pub enum FooEnum { // @has - '//*[@id="variant.MixedHiddenFirst"]//code' 'MixedHiddenFirst(_, S)' // @count - '//*[@id="variant.MixedHiddenFirst.field.0"]' 0 // @has - '//*[@id="variant.MixedHiddenFirst.field.1"]' '1: S' - MixedHiddenFirst(#[doc(hidden)] H, S), + MixedHiddenFirst(#[doc(hidden)] H, /** dox */ S), // @has - '//*[@id="variant.MixedHiddenLast"]//code' 'MixedHiddenLast(S, _)' // @has - '//*[@id="variant.MixedHiddenLast.field.0"]' '0: S' // @count - '//*[@id="variant.MixedHiddenLast.field.1"]' 0 - MixedHiddenLast(S, #[doc(hidden)] H), + MixedHiddenLast(/** dox */ S, #[doc(hidden)] H), // @has - '//*[@id="variant.HiddenStruct"]//code' 'HiddenStruct' // @count - '//*[@id="variant.HiddenStruct.field.h"]' 0 // @has - '//*[@id="variant.HiddenStruct.field.s"]' 's: S' HiddenStruct { #[doc(hidden)] h: H, + /// dox s: S, }, } diff --git a/src/test/rustdoc/macro-generated-macro.rs b/src/test/rustdoc/macro-generated-macro.rs new file mode 100644 index 00000000000..25d8bc3ec62 --- /dev/null +++ b/src/test/rustdoc/macro-generated-macro.rs @@ -0,0 +1,14 @@ +macro_rules! outer { + ($($matcher:tt)*) => { + #[macro_export] + macro_rules! inner { + (<= $($matcher)* =>) => {}; + } + } +} + +// @has macro_generated_macro/macro.inner.html //pre 'macro_rules! inner {' +// @has - //pre '(<= type $($i : ident) :: * + $e : expr =>) => { ... };' +outer!(type $($i:ident)::* + $e:expr); + +inner!(<= type foo::bar + x.sort() =>); diff --git a/src/test/rustdoc/macros.rs b/src/test/rustdoc/macros.rs index 1cd454720e7..ae0cf7a1478 100644 --- a/src/test/rustdoc/macros.rs +++ b/src/test/rustdoc/macros.rs @@ -1,7 +1,7 @@ // @has macros/macro.my_macro.html //pre 'macro_rules! my_macro {' // @has - //pre '() => { ... };' -// @has - //pre '($a : tt) => { ... };' -// @has - //pre '($e : expr) => { ... };' +// @has - //pre '($a:tt) => { ... };' +// @has - //pre '($e:expr) => { ... };' #[macro_export] macro_rules! my_macro { () => []; @@ -12,8 +12,8 @@ macro_rules! my_macro { // Check that exported macro defined in a module are shown at crate root. // @has macros/macro.my_sub_macro.html //pre 'macro_rules! my_sub_macro {' // @has - //pre '() => { ... };' -// @has - //pre '($a : tt) => { ... };' -// @has - //pre '($e : expr) => { ... };' +// @has - //pre '($a:tt) => { ... };' +// @has - //pre '($e:expr) => { ... };' mod sub { #[macro_export] macro_rules! my_sub_macro { diff --git a/src/test/rustdoc/mixing-doc-comments-and-attrs.S1_top-doc.html b/src/test/rustdoc/mixing-doc-comments-and-attrs.S1_top-doc.html index 69d647a92e8..8ff114b993e 100644 --- a/src/test/rustdoc/mixing-doc-comments-and-attrs.S1_top-doc.html +++ b/src/test/rustdoc/mixing-doc-comments-and-attrs.S1_top-doc.html @@ -1,4 +1,4 @@ -<div class="docblock"><p>Hello world! -Goodbye! +<div class="docblock"><p>Hello world!</p> +<p>Goodbye! Hello again!</p> </div> \ No newline at end of file diff --git a/src/test/rustdoc/mixing-doc-comments-and-attrs.S3_top-doc.html b/src/test/rustdoc/mixing-doc-comments-and-attrs.S3_top-doc.html new file mode 100644 index 00000000000..a4ee4b14186 --- /dev/null +++ b/src/test/rustdoc/mixing-doc-comments-and-attrs.S3_top-doc.html @@ -0,0 +1,3 @@ +<div class="docblock"><p>Par 1</p> +<p>Par 2</p> +</div> \ No newline at end of file diff --git a/src/test/rustdoc/mixing-doc-comments-and-attrs.rs b/src/test/rustdoc/mixing-doc-comments-and-attrs.rs index 1aedd4d107c..a27c5ae6d01 100644 --- a/src/test/rustdoc/mixing-doc-comments-and-attrs.rs +++ b/src/test/rustdoc/mixing-doc-comments-and-attrs.rs @@ -16,3 +16,10 @@ pub struct S1; #[doc = "Goodbye!"] /// Hello again! pub struct S2; + +// @has 'foo/struct.S3.html' +// @snapshot S3_top-doc - '//details[@class="rustdoc-toggle top-doc"]/div[@class="docblock"]' +/** Par 1 +*/ /// +/// Par 2 +pub struct S3; diff --git a/src/test/rustdoc/reexports-priv.rs b/src/test/rustdoc/reexports-priv.rs index 95f74180749..aea9b9f2b39 100644 --- a/src/test/rustdoc/reexports-priv.rs +++ b/src/test/rustdoc/reexports-priv.rs @@ -5,7 +5,7 @@ extern crate reexports; -// @has 'foo/macro.addr_of.html' '//*[@class="docblock item-decl"]' 'pub macro addr_of($place : expr) {' +// @has 'foo/macro.addr_of.html' '//*[@class="docblock item-decl"]' 'pub macro addr_of($place:expr) {' pub use reexports::addr_of; // @!has 'foo/macro.addr_of_crate.html' pub(crate) use reexports::addr_of_crate; @@ -61,11 +61,11 @@ use reexports::UnionLocal; pub mod outer { pub mod inner { - // @has 'foo/outer/inner/macro.addr_of.html' '//*[@class="docblock item-decl"]' 'pub macro addr_of($place : expr) {' + // @has 'foo/outer/inner/macro.addr_of.html' '//*[@class="docblock item-decl"]' 'pub macro addr_of($place:expr) {' pub use reexports::addr_of; - // @has 'foo/outer/inner/macro.addr_of_crate.html' '//*[@class="docblock item-decl"]' 'pub(crate) macro addr_of_crate($place : expr) {' + // @has 'foo/outer/inner/macro.addr_of_crate.html' '//*[@class="docblock item-decl"]' 'pub(crate) macro addr_of_crate($place:expr) {' pub(crate) use reexports::addr_of_crate; - // @has 'foo/outer/inner/macro.addr_of_super.html' '//*[@class="docblock item-decl"]' 'pub(in outer) macro addr_of_super($place : expr) {' + // @has 'foo/outer/inner/macro.addr_of_super.html' '//*[@class="docblock item-decl"]' 'pub(in outer) macro addr_of_super($place:expr) {' pub(super) use reexports::addr_of_super; // @!has 'foo/outer/inner/macro.addr_of_self.html' pub(self) use reexports::addr_of_self; diff --git a/src/test/rustdoc/reexports.rs b/src/test/rustdoc/reexports.rs index 3b315308470..7abcbfb6181 100644 --- a/src/test/rustdoc/reexports.rs +++ b/src/test/rustdoc/reexports.rs @@ -4,7 +4,7 @@ extern crate reexports; -// @has 'foo/macro.addr_of.html' '//*[@class="docblock item-decl"]' 'pub macro addr_of($place : expr) {' +// @has 'foo/macro.addr_of.html' '//*[@class="docblock item-decl"]' 'pub macro addr_of($place:expr) {' pub use reexports::addr_of; // @!has 'foo/macro.addr_of_crate.html' pub(crate) use reexports::addr_of_crate; @@ -60,7 +60,7 @@ use reexports::UnionLocal; pub mod outer { pub mod inner { - // @has 'foo/outer/inner/macro.addr_of.html' '//*[@class="docblock item-decl"]' 'pub macro addr_of($place : expr) {' + // @has 'foo/outer/inner/macro.addr_of.html' '//*[@class="docblock item-decl"]' 'pub macro addr_of($place:expr) {' pub use reexports::addr_of; // @!has 'foo/outer/inner/macro.addr_of_crate.html' pub(crate) use reexports::addr_of_crate; diff --git a/src/test/rustdoc/src-links-auto-impls.rs b/src/test/rustdoc/src-links-auto-impls.rs index f9ac836c9b1..46b8778217d 100644 --- a/src/test/rustdoc/src-links-auto-impls.rs +++ b/src/test/rustdoc/src-links-auto-impls.rs @@ -2,11 +2,11 @@ // @has foo/struct.Unsized.html // @has - '//div[@id="impl-Sized"]/h3[@class="code-header in-band"]' 'impl !Sized for Unsized' -// @!has - '//div[@id="impl-Sized"]//a[@class="srclink"]' '[src]' +// @!has - '//div[@id="impl-Sized"]//a[@class="srclink"]' 'source' // @has - '//div[@id="impl-Sync"]/h3[@class="code-header in-band"]' 'impl Sync for Unsized' -// @!has - '//div[@id="impl-Sync"]//a[@class="srclink"]' '[src]' +// @!has - '//div[@id="impl-Sync"]//a[@class="srclink"]' 'source' // @has - '//div[@id="impl-Any"]/h3[@class="code-header in-band"]' 'impl<T> Any for T' -// @has - '//div[@id="impl-Any"]//a[@class="srclink"]' '[src]' +// @has - '//div[@id="impl-Any"]//a[@class="srclink"]' 'source' pub struct Unsized { data: [u8], } diff --git a/src/test/rustdoc/structfields.rs b/src/test/rustdoc/structfields.rs index 6de198453cd..7e1cada4b98 100644 --- a/src/test/rustdoc/structfields.rs +++ b/src/test/rustdoc/structfields.rs @@ -2,7 +2,7 @@ pub struct Foo { // @has - //pre "pub a: ()" pub a: (), - // @has - //pre "// some fields omitted" + // @has - //pre "/* private fields */" // @!has - //pre "b: ()" b: (), // @!has - //pre "c: usize" @@ -16,7 +16,7 @@ pub struct Foo { pub struct Bar { // @has - //pre "pub a: ()" pub a: (), - // @!has - //pre "// some fields omitted" + // @!has - //pre "/* private fields */" } // @has structfields/enum.Qux.html @@ -29,11 +29,11 @@ pub enum Qux { b: (), // @has - //pre "c: usize" c: usize, - // @has - //pre "// some fields omitted" + // @has - //pre "/* private fields */" }, } -// @has structfields/struct.Baz.html //pre "pub struct Baz { /* fields omitted */ }" +// @has structfields/struct.Baz.html //pre "pub struct Baz { /* private fields */ }" pub struct Baz { x: u8, #[doc(hidden)] diff --git a/src/test/rustdoc/thread-local-src.rs b/src/test/rustdoc/thread-local-src.rs index 5e56bb5819a..6de35e3233b 100644 --- a/src/test/rustdoc/thread-local-src.rs +++ b/src/test/rustdoc/thread-local-src.rs @@ -1,6 +1,6 @@ #![crate_name = "foo"] -// @has foo/index.html '//a[@href="../src/foo/thread-local-src.rs.html#1-6"]' '[src]' +// @has foo/index.html '//a[@href="../src/foo/thread-local-src.rs.html#1-6"]' 'source' -// @has foo/constant.FOO.html '//a[@href="../src/foo/thread-local-src.rs.html#6"]' '[src]' +// @has foo/constant.FOO.html '//a[@href="../src/foo/thread-local-src.rs.html#6"]' 'source' thread_local!(pub static FOO: bool = false); diff --git a/src/test/rustdoc/toggle-item-contents.rs b/src/test/rustdoc/toggle-item-contents.rs index 937646987dd..c1df4613e35 100644 --- a/src/test/rustdoc/toggle-item-contents.rs +++ b/src/test/rustdoc/toggle-item-contents.rs @@ -55,7 +55,7 @@ pub union Union { // @has 'toggle_item_contents/struct.PrivStruct.html' // @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 0 -// @has - '//div[@class="docblock item-decl"]' 'fields omitted' +// @has - '//div[@class="docblock item-decl"]' '/* private fields */' pub struct PrivStruct { a: usize, b: usize, diff --git a/src/test/rustdoc/trait-src-link.rs b/src/test/rustdoc/trait-src-link.rs index 77116695690..a6367efba61 100644 --- a/src/test/rustdoc/trait-src-link.rs +++ b/src/test/rustdoc/trait-src-link.rs @@ -1,26 +1,26 @@ #![crate_name = "quix"] pub trait Foo { - // @has quix/trait.Foo.html '//a[@href="../src/quix/trait-src-link.rs.html#4"]' '[src]' + // @has quix/trait.Foo.html '//a[@href="../src/quix/trait-src-link.rs.html#4"]' 'source' fn required(); - // @has quix/trait.Foo.html '//a[@href="../src/quix/trait-src-link.rs.html#7"]' '[src]' + // @has quix/trait.Foo.html '//a[@href="../src/quix/trait-src-link.rs.html#7"]' 'source' fn provided() {} } pub struct Bar; impl Foo for Bar { - // @has quix/struct.Bar.html '//a[@href="../src/quix/trait-src-link.rs.html#14"]' '[src]' + // @has quix/struct.Bar.html '//a[@href="../src/quix/trait-src-link.rs.html#14"]' 'source' fn required() {} - // @has quix/struct.Bar.html '//a[@href="../src/quix/trait-src-link.rs.html#7"]' '[src]' + // @has quix/struct.Bar.html '//a[@href="../src/quix/trait-src-link.rs.html#7"]' 'source' } pub struct Baz; impl Foo for Baz { - // @has quix/struct.Baz.html '//a[@href="../src/quix/trait-src-link.rs.html#22"]' '[src]' + // @has quix/struct.Baz.html '//a[@href="../src/quix/trait-src-link.rs.html#22"]' 'source' fn required() {} - // @has quix/struct.Baz.html '//a[@href="../src/quix/trait-src-link.rs.html#25"]' '[src]' + // @has quix/struct.Baz.html '//a[@href="../src/quix/trait-src-link.rs.html#25"]' 'source' fn provided() {} } diff --git a/src/test/rustdoc/tuple-struct-fields-doc.rs b/src/test/rustdoc/tuple-struct-fields-doc.rs index 2e339fe8264..31426131bc2 100644 --- a/src/test/rustdoc/tuple-struct-fields-doc.rs +++ b/src/test/rustdoc/tuple-struct-fields-doc.rs @@ -24,6 +24,9 @@ pub struct Foo( // @has - '//*[@id="variant.BarVariant.field.0"]' '0: String' // @has - '//*[@id="variant.BarVariant.fields"]//*[@class="docblock"]' 'Hello docs' // @matches - '//*[@id="variant.FooVariant.fields"]/h4' '^Fields$' +// @has - '//*[@id="variant.BazVariant.fields"]//*[@class="docblock"]' 'dox' +// @has - '//*[@id="variant.OtherVariant.fields"]//*[@class="docblock"]' 'dox' +// @!matches - '//*[@id="variant.QuuxVariant.fields"]/h4' '^Tuple Fields$' pub enum Bar { BarVariant( /// Hello docs @@ -33,4 +36,15 @@ pub enum Bar { /// hello x: u32, }, + BazVariant( + String, + /// dox + u32, + ), + OtherVariant( + /// dox + String, + u32, + ), + QuuxVariant(String), } diff --git a/src/test/rustdoc/union.rs b/src/test/rustdoc/union.rs index 89186227732..5a788eb1b1c 100644 --- a/src/test/rustdoc/union.rs +++ b/src/test/rustdoc/union.rs @@ -2,7 +2,7 @@ pub union U { // @has - //pre "pub a: u8" pub a: u8, - // @has - //pre "// some fields omitted" + // @has - //pre "/* private fields */" // @!has - //pre "b: u16" b: u16, } diff --git a/src/test/ui/associated-types/substs-ppaux.rs b/src/test/ui/associated-types/substs-ppaux.rs index 66cd94d7a1b..974a1d961a0 100644 --- a/src/test/ui/associated-types/substs-ppaux.rs +++ b/src/test/ui/associated-types/substs-ppaux.rs @@ -25,7 +25,7 @@ fn foo<'z>() where &'z (): Sized { let x: () = <i8 as Foo<'static, 'static, u32>>::bar::<'static, char>; //[verbose]~^ ERROR mismatched types //[verbose]~| expected unit type `()` - //[verbose]~| found fn item `fn() {<i8 as Foo<ReStatic, ReStatic>>::bar::<ReStatic, char>}` + //[verbose]~| found fn item `fn() {<i8 as Foo<ReStatic, ReStatic, u32>>::bar::<ReStatic, char>}` //[normal]~^^^^ ERROR mismatched types //[normal]~| expected unit type `()` //[normal]~| found fn item `fn() {<i8 as Foo<'static, 'static>>::bar::<'static, char>}` diff --git a/src/test/ui/associated-types/substs-ppaux.verbose.stderr b/src/test/ui/associated-types/substs-ppaux.verbose.stderr index b831f3b7a76..cf480223da2 100644 --- a/src/test/ui/associated-types/substs-ppaux.verbose.stderr +++ b/src/test/ui/associated-types/substs-ppaux.verbose.stderr @@ -20,7 +20,7 @@ error[E0308]: mismatched types --> $DIR/substs-ppaux.rs:25:17 | LL | fn bar<'a, T>() where T: 'a {} - | --------------------------- fn() {<i8 as Foo<ReStatic, ReStatic>>::bar::<ReStatic, char>} defined here + | --------------------------- fn() {<i8 as Foo<ReStatic, ReStatic, u32>>::bar::<ReStatic, char>} defined here ... LL | let x: () = <i8 as Foo<'static, 'static, u32>>::bar::<'static, char>; | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found fn item @@ -28,7 +28,7 @@ LL | let x: () = <i8 as Foo<'static, 'static, u32>>::bar::<'static, char>; | expected due to this | = note: expected unit type `()` - found fn item `fn() {<i8 as Foo<ReStatic, ReStatic>>::bar::<ReStatic, char>}` + found fn item `fn() {<i8 as Foo<ReStatic, ReStatic, u32>>::bar::<ReStatic, char>}` help: use parentheses to call this function | LL | let x: () = <i8 as Foo<'static, 'static, u32>>::bar::<'static, char>(); diff --git a/src/test/ui/async-await/interior-with-const-generic-expr.rs b/src/test/ui/async-await/interior-with-const-generic-expr.rs new file mode 100644 index 00000000000..86ba7582d38 --- /dev/null +++ b/src/test/ui/async-await/interior-with-const-generic-expr.rs @@ -0,0 +1,26 @@ +// edition:2018 +// run-pass + +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] +#![allow(unused)] + +fn main() { + let x = test(); +} + +fn concat<const A: usize, const B: usize>(a: [f32; A], b: [f32; B]) -> [f32; A + B] { + todo!() +} + +async fn reverse<const A: usize>(x: [f32; A]) -> [f32; A] { + todo!() +} + +async fn test() { + let a = [0.0]; + let b = [1.0, 2.0]; + let ab = concat(a,b); + let ba = reverse(ab).await; + println!("{:?}", ba); +} diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs index 7a4b21f0223..2bcbd792e3a 100644 --- a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs +++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs @@ -15,6 +15,7 @@ fn main() { //~^ NOTE: Capturing m[] -> MutBorrow //~| NOTE: Min Capture m[] -> MutBorrow m[1] += 40; + //~^ NOTE: Capturing m[] -> MutBorrow }; c(); diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr index 69ec53447b8..129b26456ce 100644 --- a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr +++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr @@ -15,7 +15,7 @@ LL | | LL | | LL | | m[0] += 10; ... | -LL | | m[1] += 40; +LL | | LL | | }; | |_____^ | @@ -24,6 +24,11 @@ note: Capturing m[] -> MutBorrow | LL | m[0] += 10; | ^ +note: Capturing m[] -> MutBorrow + --> $DIR/arrays-completely-captured.rs:17:9 + | +LL | m[1] += 40; + | ^ error: Min Capture analysis includes: --> $DIR/arrays-completely-captured.rs:11:5 @@ -33,7 +38,7 @@ LL | | LL | | LL | | m[0] += 10; ... | -LL | | m[1] += 40; +LL | | LL | | }; | |_____^ | diff --git a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs index 9918802334e..6c65a7bf87b 100644 --- a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs +++ b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs @@ -15,6 +15,8 @@ fn arrays() { //~| ERROR: Min Capture analysis includes: let [a, b, .., e] = arr; //~^ NOTE: Capturing arr[Index] -> ByValue + //~| NOTE: Capturing arr[Index] -> ByValue + //~| NOTE: Capturing arr[Index] -> ByValue //~| NOTE: Min Capture arr[] -> ByValue assert_eq!(a, "A"); assert_eq!(b, "B"); diff --git a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr index b53adb52481..44fbe6d8158 100644 --- a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr +++ b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr @@ -8,7 +8,7 @@ LL | let c = #[rustc_capture_analysis] = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable error[E0658]: attributes on expressions are experimental - --> $DIR/destructure_patterns.rs:36:13 + --> $DIR/destructure_patterns.rs:38:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | let c = #[rustc_capture_analysis] = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable error[E0658]: attributes on expressions are experimental - --> $DIR/destructure_patterns.rs:56:13 + --> $DIR/destructure_patterns.rs:58:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -42,6 +42,16 @@ note: Capturing arr[Index] -> ByValue | LL | let [a, b, .., e] = arr; | ^^^ +note: Capturing arr[Index] -> ByValue + --> $DIR/destructure_patterns.rs:16:29 + | +LL | let [a, b, .., e] = arr; + | ^^^ +note: Capturing arr[Index] -> ByValue + --> $DIR/destructure_patterns.rs:16:29 + | +LL | let [a, b, .., e] = arr; + | ^^^ error: Min Capture analysis includes: --> $DIR/destructure_patterns.rs:13:5 @@ -62,7 +72,7 @@ LL | let [a, b, .., e] = arr; | ^^^ error: First Pass analysis includes: - --> $DIR/destructure_patterns.rs:39:5 + --> $DIR/destructure_patterns.rs:41:5 | LL | / || { LL | | @@ -74,18 +84,18 @@ LL | | }; | |_____^ | note: Capturing p[(0, 0)] -> MutBorrow - --> $DIR/destructure_patterns.rs:42:58 + --> $DIR/destructure_patterns.rs:44:58 | LL | let Point { x: ref mut x, y: _, id: moved_id } = p; | ^ note: Capturing p[(2, 0)] -> ByValue - --> $DIR/destructure_patterns.rs:42:58 + --> $DIR/destructure_patterns.rs:44:58 | LL | let Point { x: ref mut x, y: _, id: moved_id } = p; | ^ error: Min Capture analysis includes: - --> $DIR/destructure_patterns.rs:39:5 + --> $DIR/destructure_patterns.rs:41:5 | LL | / || { LL | | @@ -97,18 +107,18 @@ LL | | }; | |_____^ | note: Min Capture p[(0, 0)] -> MutBorrow - --> $DIR/destructure_patterns.rs:42:58 + --> $DIR/destructure_patterns.rs:44:58 | LL | let Point { x: ref mut x, y: _, id: moved_id } = p; | ^ note: Min Capture p[(2, 0)] -> ByValue - --> $DIR/destructure_patterns.rs:42:58 + --> $DIR/destructure_patterns.rs:44:58 | LL | let Point { x: ref mut x, y: _, id: moved_id } = p; | ^ error: First Pass analysis includes: - --> $DIR/destructure_patterns.rs:59:5 + --> $DIR/destructure_patterns.rs:61:5 | LL | / || { LL | | @@ -120,23 +130,23 @@ LL | | }; | |_____^ | note: Capturing t[(0, 0)] -> MutBorrow - --> $DIR/destructure_patterns.rs:62:54 + --> $DIR/destructure_patterns.rs:64:54 | LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; | ^ note: Capturing t[(1, 0)] -> ImmBorrow - --> $DIR/destructure_patterns.rs:62:54 + --> $DIR/destructure_patterns.rs:64:54 | LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; | ^ note: Capturing t[(2, 0),(0, 0)] -> ByValue - --> $DIR/destructure_patterns.rs:62:54 + --> $DIR/destructure_patterns.rs:64:54 | LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; | ^ error: Min Capture analysis includes: - --> $DIR/destructure_patterns.rs:59:5 + --> $DIR/destructure_patterns.rs:61:5 | LL | / || { LL | | @@ -148,17 +158,17 @@ LL | | }; | |_____^ | note: Min Capture t[(0, 0)] -> MutBorrow - --> $DIR/destructure_patterns.rs:62:54 + --> $DIR/destructure_patterns.rs:64:54 | LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; | ^ note: Min Capture t[(1, 0)] -> ImmBorrow - --> $DIR/destructure_patterns.rs:62:54 + --> $DIR/destructure_patterns.rs:64:54 | LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; | ^ note: Min Capture t[(2, 0),(0, 0)] -> ByValue - --> $DIR/destructure_patterns.rs:62:54 + --> $DIR/destructure_patterns.rs:64:54 | LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; | ^ diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/union.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/union.rs new file mode 100644 index 00000000000..46b54846e32 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/union.rs @@ -0,0 +1,25 @@ +// edition:2021 + +// Test that we point to the correct location that results a union being captured. +// Union is special because it can't be disjointly captured. + +union A { + y: u32, + x: (), +} + +fn main() { + let mut a = A { y: 1 }; + let mut c = || { + //~^ borrow of `a.y` occurs here + let _ = unsafe { &a.y }; + let _ = &mut a; + //~^ borrow occurs due to use in closure + let _ = unsafe { &mut a.y }; + }; + a.y = 1; + //~^ cannot assign to `a.y` because it is borrowed [E0506] + //~| assignment to borrowed `a.y` occurs here + c(); + //~^ borrow later used here +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/union.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/union.stderr new file mode 100644 index 00000000000..7c34e2336c8 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/union.stderr @@ -0,0 +1,18 @@ +error[E0506]: cannot assign to `a.y` because it is borrowed + --> $DIR/union.rs:20:5 + | +LL | let mut c = || { + | -- borrow of `a.y` occurs here +... +LL | let _ = &mut a; + | - borrow occurs due to use in closure +... +LL | a.y = 1; + | ^^^^^^^ assignment to borrowed `a.y` occurs here +... +LL | c(); + | - borrow later used here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0506`. diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.rs b/src/test/ui/closures/2229_closure_analysis/nested-closure.rs index f6775b3a3a5..22eae744b80 100644 --- a/src/test/ui/closures/2229_closure_analysis/nested-closure.rs +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.rs @@ -40,6 +40,7 @@ fn main() { //~| NOTE: Min Capture p[(1, 0)] -> MutBorrow c2(); println!("{}", p.y); + //~^ NOTE: Capturing p[(1, 0)] -> ImmBorrow }; c1(); diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr b/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr index 013bc74e67e..a50d0c6a182 100644 --- a/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr @@ -58,7 +58,7 @@ LL | | LL | | LL | | println!("{}", p.x); ... | -LL | | println!("{}", p.y); +LL | | LL | | }; | |_____^ | @@ -72,6 +72,11 @@ note: Capturing p[(1, 0)] -> MutBorrow | LL | || p.y += incr; | ^^^ +note: Capturing p[(1, 0)] -> ImmBorrow + --> $DIR/nested-closure.rs:42:24 + | +LL | println!("{}", p.y); + | ^^^ error: Min Capture analysis includes: --> $DIR/nested-closure.rs:22:5 @@ -81,7 +86,7 @@ LL | | LL | | LL | | println!("{}", p.x); ... | -LL | | println!("{}", p.y); +LL | | LL | | }; | |_____^ | diff --git a/src/test/ui/closures/2229_closure_analysis/repr_packed.rs b/src/test/ui/closures/2229_closure_analysis/repr_packed.rs index 7d472ad020f..3ed780f51c7 100644 --- a/src/test/ui/closures/2229_closure_analysis/repr_packed.rs +++ b/src/test/ui/closures/2229_closure_analysis/repr_packed.rs @@ -48,6 +48,7 @@ fn test_alignment_affected() { //~^ ERROR: First Pass analysis includes: //~| ERROR: Min Capture analysis includes: let z1: &String = &foo.x; + //~^ NOTE: Capturing foo[] -> ImmBorrow let z2: &mut u16 = &mut foo.y; //~^ NOTE: Capturing foo[] -> MutBorrow //~| NOTE: Min Capture foo[] -> MutBorrow diff --git a/src/test/ui/closures/2229_closure_analysis/repr_packed.stderr b/src/test/ui/closures/2229_closure_analysis/repr_packed.stderr index 405f66210aa..580061ebc6e 100644 --- a/src/test/ui/closures/2229_closure_analysis/repr_packed.stderr +++ b/src/test/ui/closures/2229_closure_analysis/repr_packed.stderr @@ -17,7 +17,7 @@ LL | let mut c = #[rustc_capture_analysis] = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable error[E0658]: attributes on expressions are experimental - --> $DIR/repr_packed.rs:78:13 + --> $DIR/repr_packed.rs:79:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -83,8 +83,13 @@ LL | | println!("({}, {})", z1, z2); LL | | }; | |_____^ | +note: Capturing foo[] -> ImmBorrow + --> $DIR/repr_packed.rs:50:28 + | +LL | let z1: &String = &foo.x; + | ^^^^^ note: Capturing foo[] -> MutBorrow - --> $DIR/repr_packed.rs:51:33 + --> $DIR/repr_packed.rs:52:33 | LL | let z2: &mut u16 = &mut foo.y; | ^^^^^ @@ -102,13 +107,13 @@ LL | | }; | |_____^ | note: Min Capture foo[] -> MutBorrow - --> $DIR/repr_packed.rs:51:33 + --> $DIR/repr_packed.rs:52:33 | LL | let z2: &mut u16 = &mut foo.y; | ^^^^^ error: First Pass analysis includes: - --> $DIR/repr_packed.rs:81:5 + --> $DIR/repr_packed.rs:82:5 | LL | / || { LL | | @@ -120,18 +125,18 @@ LL | | }; | |_____^ | note: Capturing foo[] -> ImmBorrow - --> $DIR/repr_packed.rs:84:24 + --> $DIR/repr_packed.rs:85:24 | LL | println!("{}", foo.x); | ^^^^^ note: Capturing foo[(0, 0)] -> ByValue - --> $DIR/repr_packed.rs:88:18 + --> $DIR/repr_packed.rs:89:18 | LL | let _z = foo.x; | ^^^^^ error: Min Capture analysis includes: - --> $DIR/repr_packed.rs:81:5 + --> $DIR/repr_packed.rs:82:5 | LL | / || { LL | | @@ -143,7 +148,7 @@ LL | | }; | |_____^ | note: Min Capture foo[] -> ByValue - --> $DIR/repr_packed.rs:84:24 + --> $DIR/repr_packed.rs:85:24 | LL | println!("{}", foo.x); | ^^^^^ foo[] used here diff --git a/src/test/ui/const-generics/generic_arg_infer/infer_arg_and_const_arg.rs b/src/test/ui/const-generics/generic_arg_infer/infer_arg_and_const_arg.rs new file mode 100644 index 00000000000..23c8d753752 --- /dev/null +++ b/src/test/ui/const-generics/generic_arg_infer/infer_arg_and_const_arg.rs @@ -0,0 +1,12 @@ +// check-pass +#![feature(generic_arg_infer)] + +struct Foo<const N: bool, const M: u8>; +struct Bar<const N: u8, const M: u32>; + +fn main() { + let _: Foo<true, _> = Foo::<_, 1>; + let _: Foo<_, 1> = Foo::<true, _>; + let _: Bar<1, _> = Bar::<_, 300>; + let _: Bar<_, 300> = Bar::<1, _>; +} diff --git a/src/test/ui/const-generics/issues/issue-62878.full.stderr b/src/test/ui/const-generics/issues/issue-62878.full.stderr index f074a65313f..3a2b291d7ba 100644 --- a/src/test/ui/const-generics/issues/issue-62878.full.stderr +++ b/src/test/ui/const-generics/issues/issue-62878.full.stderr @@ -4,13 +4,6 @@ error[E0770]: the type of const parameters must not depend on other generic para LL | fn foo<const N: usize, const A: [u8; N]>() {} | ^ the type must not depend on the parameter `N` -error[E0308]: mismatched types - --> $DIR/issue-62878.rs:10:15 - | -LL | foo::<_, {[1]}>(); - | ^^^ expected `usize`, found array `[{integer}; 1]` - -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0308, E0770. -For more information about an error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0770`. diff --git a/src/test/ui/const-generics/issues/issue-62878.rs b/src/test/ui/const-generics/issues/issue-62878.rs index 38f5ff77b56..578ce765b2f 100644 --- a/src/test/ui/const-generics/issues/issue-62878.rs +++ b/src/test/ui/const-generics/issues/issue-62878.rs @@ -7,6 +7,5 @@ fn foo<const N: usize, const A: [u8; N]>() {} //[min]~| ERROR `[u8; _]` is forbidden as the type of a const generic parameter fn main() { - foo::<_, {[1]}>(); - //[full]~^ ERROR mismatched types + foo::<_, { [1] }>(); } diff --git a/src/test/ui/foreign/issue-91370-foreign-fn-block-impl.rs b/src/test/ui/foreign/issue-91370-foreign-fn-block-impl.rs new file mode 100644 index 00000000000..2ac3ca29355 --- /dev/null +++ b/src/test/ui/foreign/issue-91370-foreign-fn-block-impl.rs @@ -0,0 +1,12 @@ +// Regression test for issue #91370. + +extern { + //~^ `extern` blocks define existing foreign functions + fn f() { + //~^ incorrect function inside `extern` block + //~| cannot have a body + impl Copy for u8 {} + } +} + +fn main() {} diff --git a/src/test/ui/foreign/issue-91370-foreign-fn-block-impl.stderr b/src/test/ui/foreign/issue-91370-foreign-fn-block-impl.stderr new file mode 100644 index 00000000000..4fb2f8c659c --- /dev/null +++ b/src/test/ui/foreign/issue-91370-foreign-fn-block-impl.stderr @@ -0,0 +1,21 @@ +error: incorrect function inside `extern` block + --> $DIR/issue-91370-foreign-fn-block-impl.rs:5:8 + | +LL | extern { + | ------ `extern` blocks define existing foreign functions and functions inside of them cannot have a body +LL | +LL | fn f() { + | ________^___- + | | | + | | cannot have a body +LL | | +LL | | +LL | | impl Copy for u8 {} +LL | | } + | |_____- help: remove the invalid body: `;` + | + = help: you might have meant to write a function accessible through FFI, which can be done by writing `extern fn` outside of the `extern` block + = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html + +error: aborting due to previous error + diff --git a/src/test/ui/generic-associated-types/issue-89352.rs b/src/test/ui/generic-associated-types/issue-89352.rs new file mode 100644 index 00000000000..d9c656d5f58 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-89352.rs @@ -0,0 +1,32 @@ +// check-pass + +#![feature(generic_associated_types)] + +use std::marker::PhantomData; + +pub trait GenAssoc<T> { + type Iter<'at>; + fn iter(&self) -> Self::Iter<'_>; + fn reborrow<'longt: 'shortt, 'shortt>(iter: Self::Iter<'longt>) -> Self::Iter<'shortt>; +} + +pub struct Wrapper<'a, T: 'a, A: GenAssoc<T>> { + a: A::Iter<'a>, + _p: PhantomData<T>, +} + +impl<'ai, T: 'ai, A: GenAssoc<T>> GenAssoc<T> for Wrapper<'ai, T, A> +where + A::Iter<'ai>: Clone, +{ + type Iter<'b> = (); + fn iter<'s>(&'s self) -> Self::Iter<'s> { + let a = A::reborrow::<'ai, 's>(self.a.clone()); + } + + fn reborrow<'long: 'short, 'short>(iter: Self::Iter<'long>) -> Self::Iter<'short> { + () + } +} + +fn main() {} diff --git a/src/test/ui/infinite/infinite-autoderef.stderr b/src/test/ui/infinite/infinite-autoderef.stderr index 03e4718f5df..2d29f0592e1 100644 --- a/src/test/ui/infinite/infinite-autoderef.stderr +++ b/src/test/ui/infinite/infinite-autoderef.stderr @@ -2,9 +2,12 @@ error[E0308]: mismatched types --> $DIR/infinite-autoderef.rs:20:13 | LL | x = Box::new(x); - | ^^^^^^^^^^^- help: try using a conversion method: `.to_string()` - | | - | cyclic type of infinite size + | ^^^^^^^^^^^ cyclic type of infinite size + | +help: consider unboxing the value + | +LL | x = *Box::new(x); + | + error[E0055]: reached the recursion limit while auto-dereferencing `Foo` --> $DIR/infinite-autoderef.rs:25:5 diff --git a/src/test/ui/lint/lint-pub-unreachable-for-nested-glob.rs b/src/test/ui/lint/lint-pub-unreachable-for-nested-glob.rs new file mode 100644 index 00000000000..2df6d08e7ae --- /dev/null +++ b/src/test/ui/lint/lint-pub-unreachable-for-nested-glob.rs @@ -0,0 +1,28 @@ +// check-pass + +#![deny(unreachable_pub)] + +pub use self::m1::*; + +mod m1 { + pub use self::m2::*; + + mod m2 { + pub struct Item1; + pub struct Item2; + } +} + + +pub use self::o1::{ Item42, Item24 }; + +mod o1 { + pub use self::o2::{ Item42, Item24 }; + + mod o2 { + pub struct Item42; + pub struct Item24; + } +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr index 983d6a06afa..459198eec5a 100644 --- a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr @@ -6,7 +6,7 @@ LL | with_signature(x, |mut y| Box::new(y.next())) | = note: defining type: no_region::<'_#1r, T>::{closure#0} with closure substs [ i32, - extern "rust-call" fn((std::boxed::Box<T>,)) -> std::boxed::Box<(dyn Anything + '_#2r)>, + extern "rust-call" fn((std::boxed::Box<T, std::alloc::Global>,)) -> std::boxed::Box<(dyn Anything + '_#2r), std::alloc::Global>, (), ] = note: number of external vids: 3 @@ -42,7 +42,7 @@ LL | with_signature(x, |mut y| Box::new(y.next())) | = note: defining type: correct_region::<'_#1r, T>::{closure#0} with closure substs [ i32, - extern "rust-call" fn((std::boxed::Box<T>,)) -> std::boxed::Box<(dyn Anything + '_#2r)>, + extern "rust-call" fn((std::boxed::Box<T, std::alloc::Global>,)) -> std::boxed::Box<(dyn Anything + '_#2r), std::alloc::Global>, (), ] = note: number of external vids: 3 @@ -69,7 +69,7 @@ LL | with_signature(x, |mut y| Box::new(y.next())) | = note: defining type: wrong_region::<'_#1r, '_#2r, T>::{closure#0} with closure substs [ i32, - extern "rust-call" fn((std::boxed::Box<T>,)) -> std::boxed::Box<(dyn Anything + '_#3r)>, + extern "rust-call" fn((std::boxed::Box<T, std::alloc::Global>,)) -> std::boxed::Box<(dyn Anything + '_#3r), std::alloc::Global>, (), ] = note: number of external vids: 4 @@ -105,7 +105,7 @@ LL | with_signature(x, |mut y| Box::new(y.next())) | = note: defining type: outlives_region::<'_#1r, '_#2r, T>::{closure#0} with closure substs [ i32, - extern "rust-call" fn((std::boxed::Box<T>,)) -> std::boxed::Box<(dyn Anything + '_#3r)>, + extern "rust-call" fn((std::boxed::Box<T, std::alloc::Global>,)) -> std::boxed::Box<(dyn Anything + '_#3r), std::alloc::Global>, (), ] = note: number of external vids: 4 diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr index 88d73e7a729..6e8b3021d33 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr @@ -6,7 +6,7 @@ LL | with_signature(x, |y| y) | = note: defining type: no_region::<'_#1r, T>::{closure#0} with closure substs [ i32, - extern "rust-call" fn((std::boxed::Box<T>,)) -> std::boxed::Box<(dyn std::fmt::Debug + '_#2r)>, + extern "rust-call" fn((std::boxed::Box<T, std::alloc::Global>,)) -> std::boxed::Box<(dyn std::fmt::Debug + '_#2r), std::alloc::Global>, (), ] = note: number of external vids: 3 diff --git a/src/test/ui/occurs-check-2.stderr b/src/test/ui/occurs-check-2.stderr index dcbfc81b4d5..b68c3fa5bcc 100644 --- a/src/test/ui/occurs-check-2.stderr +++ b/src/test/ui/occurs-check-2.stderr @@ -2,9 +2,12 @@ error[E0308]: mismatched types --> $DIR/occurs-check-2.rs:7:9 | LL | f = Box::new(g); - | ^^^^^^^^^^^- help: try using a conversion method: `.to_string()` - | | - | cyclic type of infinite size + | ^^^^^^^^^^^ cyclic type of infinite size + | +help: consider unboxing the value + | +LL | f = *Box::new(g); + | + error: aborting due to previous error diff --git a/src/test/ui/occurs-check.stderr b/src/test/ui/occurs-check.stderr index 3e1ef2e719a..fdbbdc3abb3 100644 --- a/src/test/ui/occurs-check.stderr +++ b/src/test/ui/occurs-check.stderr @@ -2,9 +2,12 @@ error[E0308]: mismatched types --> $DIR/occurs-check.rs:5:9 | LL | f = Box::new(f); - | ^^^^^^^^^^^- help: try using a conversion method: `.to_string()` - | | - | cyclic type of infinite size + | ^^^^^^^^^^^ cyclic type of infinite size + | +help: consider unboxing the value + | +LL | f = *Box::new(f); + | + error: aborting due to previous error diff --git a/src/test/ui/privacy/auxiliary/issue-92755.rs b/src/test/ui/privacy/auxiliary/issue-92755.rs new file mode 100644 index 00000000000..6f85273461a --- /dev/null +++ b/src/test/ui/privacy/auxiliary/issue-92755.rs @@ -0,0 +1,17 @@ +mod machine { + pub struct A { + pub b: B, + } + pub struct B {} + impl B { + pub fn f(&self) {} + } +} + +pub struct Context { + pub a: machine::A, +} + +pub fn ctx() -> Context { + todo!(); +} diff --git a/src/test/ui/privacy/issue-92755.rs b/src/test/ui/privacy/issue-92755.rs new file mode 100644 index 00000000000..49559152b6f --- /dev/null +++ b/src/test/ui/privacy/issue-92755.rs @@ -0,0 +1,10 @@ +// aux-build:issue-92755.rs +// build-pass + +// Thank you @tmiasko for providing the content of this test! + +extern crate issue_92755; + +fn main() { + issue_92755::ctx().a.b.f(); +} diff --git a/src/test/ui/return/tail-expr-as-potential-return.rs b/src/test/ui/return/tail-expr-as-potential-return.rs index 83266abfa06..2c3610fb24d 100644 --- a/src/test/ui/return/tail-expr-as-potential-return.rs +++ b/src/test/ui/return/tail-expr-as-potential-return.rs @@ -1,3 +1,16 @@ +// > Suggest `return`ing tail expressions that match return type +// > +// > Some newcomers are confused by the behavior of tail expressions, +// > interpreting that "leaving out the `;` makes it the return value". +// > To help them go in the right direction, suggest using `return` instead +// > when applicable. +// (original commit description for this test) +// +// This test was amended to also serve as a regression test for #92308, where +// this suggestion would not trigger with async functions. +// +// edition:2018 + fn main() { let _ = foo(true); } @@ -5,6 +18,15 @@ fn main() { fn foo(x: bool) -> Result<f64, i32> { if x { Err(42) //~ ERROR mismatched types + //| HELP you might have meant to return this value + } + Ok(42.0) +} + +async fn bar(x: bool) -> Result<f64, i32> { + if x { + Err(42) //~ ERROR mismatched types + //| HELP you might have meant to return this value } Ok(42.0) } diff --git a/src/test/ui/return/tail-expr-as-potential-return.stderr b/src/test/ui/return/tail-expr-as-potential-return.stderr index 87ef18878d6..dec1cbc4624 100644 --- a/src/test/ui/return/tail-expr-as-potential-return.stderr +++ b/src/test/ui/return/tail-expr-as-potential-return.stderr @@ -1,9 +1,10 @@ error[E0308]: mismatched types - --> $DIR/tail-expr-as-potential-return.rs:7:9 + --> $DIR/tail-expr-as-potential-return.rs:28:9 | LL | / if x { LL | | Err(42) | | ^^^^^^^ expected `()`, found enum `Result` +LL | | //| HELP you might have meant to return this value LL | | } | |_____- expected this to be `()` | @@ -14,6 +15,23 @@ help: you might have meant to return this value LL | return Err(42); | ++++++ + -error: aborting due to previous error +error[E0308]: mismatched types + --> $DIR/tail-expr-as-potential-return.rs:20:9 + | +LL | / if x { +LL | | Err(42) + | | ^^^^^^^ expected `()`, found enum `Result` +LL | | //| HELP you might have meant to return this value +LL | | } + | |_____- expected this to be `()` + | + = note: expected unit type `()` + found enum `Result<_, {integer}>` +help: you might have meant to return this value + | +LL | return Err(42); + | ++++++ + + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.rs b/src/test/ui/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.rs new file mode 100644 index 00000000000..cccb856c2f6 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.rs @@ -0,0 +1,17 @@ +#![feature(const_fn_trait_bound)] +#![feature(const_trait_impl)] + +pub trait Tr { + #[default_method_body_is_const] + fn a(&self) {} + + #[default_method_body_is_const] + fn b(&self) { + ().a() + //~^ ERROR calls in constant functions are limited + } +} + +impl Tr for () {} + +fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.stderr b/src/test/ui/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.stderr new file mode 100644 index 00000000000..91f4d2fd4b0 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.stderr @@ -0,0 +1,9 @@ +error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants + --> $DIR/default-method-body-is-const-same-trait-ck.rs:10:9 + | +LL | ().a() + | ^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0015`. diff --git a/src/test/ui/span/coerce-suggestions.stderr b/src/test/ui/span/coerce-suggestions.stderr index 74caae8645c..5a964c5d5cc 100644 --- a/src/test/ui/span/coerce-suggestions.stderr +++ b/src/test/ui/span/coerce-suggestions.stderr @@ -38,9 +38,12 @@ error[E0308]: mismatched types --> $DIR/coerce-suggestions.rs:17:9 | LL | f = Box::new(f); - | ^^^^^^^^^^^- help: try using a conversion method: `.to_string()` - | | - | cyclic type of infinite size + | ^^^^^^^^^^^ cyclic type of infinite size + | +help: consider unboxing the value + | +LL | f = *Box::new(f); + | + error[E0308]: mismatched types --> $DIR/coerce-suggestions.rs:21:9 diff --git a/src/test/ui/span/impl-wrong-item-for-trait.stderr b/src/test/ui/span/impl-wrong-item-for-trait.stderr index de200ca0721..d805bbc7926 100644 --- a/src/test/ui/span/impl-wrong-item-for-trait.stderr +++ b/src/test/ui/span/impl-wrong-item-for-trait.stderr @@ -4,6 +4,15 @@ error[E0437]: type `bar` is not a member of trait `Foo` LL | type bar = u64; | ^^^^^^^^^^^^^^^ not a member of trait `Foo` +error[E0324]: item `MY_CONST` is an associated method, which doesn't match its trait `Foo` + --> $DIR/impl-wrong-item-for-trait.rs:22:5 + | +LL | const MY_CONST: u32; + | -------------------- item in trait +... +LL | fn MY_CONST() {} + | ^^^^^^^^^^^^^^^^ does not match trait + error[E0323]: item `bar` is an associated const, which doesn't match its trait `Foo` --> $DIR/impl-wrong-item-for-trait.rs:12:5 | @@ -13,6 +22,15 @@ LL | fn bar(&self); LL | const bar: u64 = 1; | ^^^^^^^^^^^^^^^^^^^ does not match trait +error[E0325]: item `bar` is an associated type, which doesn't match its trait `Foo` + --> $DIR/impl-wrong-item-for-trait.rs:30:5 + | +LL | fn bar(&self); + | -------------- item in trait +... +LL | type bar = u64; + | ^^^^^^^^^^^^^^^ does not match trait + error[E0046]: not all trait items implemented, missing: `bar` --> $DIR/impl-wrong-item-for-trait.rs:10:1 | @@ -22,15 +40,6 @@ LL | fn bar(&self); LL | impl Foo for FooConstForMethod { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `bar` in implementation -error[E0324]: item `MY_CONST` is an associated method, which doesn't match its trait `Foo` - --> $DIR/impl-wrong-item-for-trait.rs:22:5 - | -LL | const MY_CONST: u32; - | -------------------- item in trait -... -LL | fn MY_CONST() {} - | ^^^^^^^^^^^^^^^^ does not match trait - error[E0046]: not all trait items implemented, missing: `MY_CONST` --> $DIR/impl-wrong-item-for-trait.rs:19:1 | @@ -40,15 +49,6 @@ LL | const MY_CONST: u32; LL | impl Foo for FooMethodForConst { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `MY_CONST` in implementation -error[E0325]: item `bar` is an associated type, which doesn't match its trait `Foo` - --> $DIR/impl-wrong-item-for-trait.rs:30:5 - | -LL | fn bar(&self); - | -------------- item in trait -... -LL | type bar = u64; - | ^^^^^^^^^^^^^^^ does not match trait - error[E0046]: not all trait items implemented, missing: `bar` --> $DIR/impl-wrong-item-for-trait.rs:28:1 | diff --git a/src/test/ui/suggestions/boxed-variant-field.rs b/src/test/ui/suggestions/boxed-variant-field.rs index 9b9e70a675f..e79be2f6127 100644 --- a/src/test/ui/suggestions/boxed-variant-field.rs +++ b/src/test/ui/suggestions/boxed-variant-field.rs @@ -8,7 +8,7 @@ fn foo(x: Ty) -> Ty { Ty::Unit => Ty::Unit, Ty::List(elem) => foo(elem), //~^ ERROR mismatched types - //~| HELP try dereferencing the `Box` + //~| HELP consider unboxing the value //~| HELP try wrapping } } diff --git a/src/test/ui/suggestions/boxed-variant-field.stderr b/src/test/ui/suggestions/boxed-variant-field.stderr index e865b993a4c..9a31dc89197 100644 --- a/src/test/ui/suggestions/boxed-variant-field.stderr +++ b/src/test/ui/suggestions/boxed-variant-field.stderr @@ -6,7 +6,7 @@ LL | Ty::List(elem) => foo(elem), | = note: expected enum `Ty` found struct `Box<Ty>` -help: try dereferencing the `Box` +help: consider unboxing the value | LL | Ty::List(elem) => foo(*elem), | + diff --git a/src/test/ui/terr-sorts.stderr b/src/test/ui/terr-sorts.stderr index 65d90678040..34d4d9eaded 100644 --- a/src/test/ui/terr-sorts.stderr +++ b/src/test/ui/terr-sorts.stderr @@ -6,7 +6,7 @@ LL | want_foo(b); | = note: expected struct `Foo` found struct `Box<Foo>` -help: try dereferencing the `Box` +help: consider unboxing the value | LL | want_foo(*b); | + diff --git a/src/test/ui/traits/pointee-deduction.rs b/src/test/ui/traits/pointee-deduction.rs new file mode 100644 index 00000000000..f888246967d --- /dev/null +++ b/src/test/ui/traits/pointee-deduction.rs @@ -0,0 +1,22 @@ +// run-pass + +#![feature(ptr_metadata)] + +use std::alloc::Layout; +use std::ptr::Pointee; + +trait Foo { + type Bar; +} + +impl Foo for () { + type Bar = (); +} + +struct Wrapper1<T: Foo>(<T as Foo>::Bar); +struct Wrapper2<T: Foo>(<Wrapper1<T> as Pointee>::Metadata); + +fn main() { + let _: Wrapper2<()> = Wrapper2(()); + let _ = Layout::new::<Wrapper2<()>>(); +} diff --git a/src/test/ui/type-alias-impl-trait/bound_reduction2.rs b/src/test/ui/type-alias-impl-trait/bound_reduction2.rs index 579067340e8..cee8186dd8f 100644 --- a/src/test/ui/type-alias-impl-trait/bound_reduction2.rs +++ b/src/test/ui/type-alias-impl-trait/bound_reduction2.rs @@ -15,9 +15,5 @@ impl<W> Trait<W> for () {} fn foo_desugared<T: TraitWithAssoc>(_: T) -> Foo<T::Assoc> { //~^ ERROR non-defining opaque type use in defining scope - //~| ERROR non-defining opaque type use in defining scope - //~| ERROR non-defining opaque type use in defining scope - //~| ERROR `T` is part of concrete type but not used in parameter list - //~| ERROR `T` is part of concrete type but not used in parameter list () } diff --git a/src/test/ui/type-alias-impl-trait/bound_reduction2.stderr b/src/test/ui/type-alias-impl-trait/bound_reduction2.stderr index a77c0000f12..03e696fe898 100644 --- a/src/test/ui/type-alias-impl-trait/bound_reduction2.stderr +++ b/src/test/ui/type-alias-impl-trait/bound_reduction2.stderr @@ -1,34 +1,8 @@ -error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias - --> $DIR/bound_reduction2.rs:16:60 - | -LL | fn foo_desugared<T: TraitWithAssoc>(_: T) -> Foo<T::Assoc> { - | ____________________________________________________________^ -LL | | -LL | | -LL | | -... | -LL | | () -LL | | } - | |_^ - -error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias - --> $DIR/bound_reduction2.rs:16:60 - | -LL | fn foo_desugared<T: TraitWithAssoc>(_: T) -> Foo<T::Assoc> { - | ____________________________________________________________^ -LL | | -LL | | -LL | | -... | -LL | | () -LL | | } - | |_^ - error: non-defining opaque type use in defining scope - --> $DIR/bound_reduction2.rs:16:1 + --> $DIR/bound_reduction2.rs:16:46 | LL | fn foo_desugared<T: TraitWithAssoc>(_: T) -> Foo<T::Assoc> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ | note: used non-generic type `<T as TraitWithAssoc>::Assoc` for generic parameter --> $DIR/bound_reduction2.rs:9:10 @@ -36,35 +10,11 @@ note: used non-generic type `<T as TraitWithAssoc>::Assoc` for generic parameter LL | type Foo<V> = impl Trait<V>; | ^ -error: non-defining opaque type use in defining scope - --> $DIR/bound_reduction2.rs:16:1 - | -LL | fn foo_desugared<T: TraitWithAssoc>(_: T) -> Foo<T::Assoc> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: used non-generic type `_` for generic parameter - --> $DIR/bound_reduction2.rs:9:10 - | -LL | type Foo<V> = impl Trait<V>; - | ^ - -error: non-defining opaque type use in defining scope - --> $DIR/bound_reduction2.rs:16:1 - | -LL | fn foo_desugared<T: TraitWithAssoc>(_: T) -> Foo<T::Assoc> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: used non-generic type `_` for generic parameter - --> $DIR/bound_reduction2.rs:9:10 - | -LL | type Foo<V> = impl Trait<V>; - | ^ - error: could not find defining uses --> $DIR/bound_reduction2.rs:9:15 | LL | type Foo<V> = impl Trait<V>; | ^^^^^^^^^^^^^ -error: aborting due to 6 previous errors +error: aborting due to 2 previous errors diff --git a/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.nll.stderr b/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.nll.stderr index 8c9cb742fac..a4ccae4eb7e 100644 --- a/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.nll.stderr +++ b/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.nll.stderr @@ -10,5 +10,29 @@ error: higher-ranked subtype error LL | |x| x | ^^^^^ -error: aborting due to 2 previous errors +error[E0308]: mismatched types + --> $DIR/issue-57611-trait-alias.rs:17:16 + | +LL | type Bar = impl Baz<Self, Self>; + | ^^^^^^^^^^^^^^^^^^^^ one type is more general than the other + | + = note: expected type `for<'r> Fn<(&'r X,)>` + found type `Fn<(&'static X,)>` +note: this closure does not fulfill the lifetime requirements + --> $DIR/issue-57611-trait-alias.rs:21:9 + | +LL | |x| x + | ^^^^^ + +error: implementation of `FnOnce` is not general enough + --> $DIR/issue-57611-trait-alias.rs:17:16 + | +LL | type Bar = impl Baz<Self, Self>; + | ^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&'static X) -> &'static X` must implement `FnOnce<(&'0 X,)>`, for any lifetime `'0`... + = note: ...but it actually implements `FnOnce<(&'static X,)>` + +error: aborting due to 4 previous errors +For more information about this error, try `rustc --explain E0308`. diff --git a/src/tools/cargo b/src/tools/cargo -Subproject 358e79fe56fe374649275ca7aebaafd57ade0e8 +Subproject 06b9d31743210b788b130c8a484c2838afa6fc2 diff --git a/src/tools/clippy/.cargo/config.toml b/src/tools/clippy/.cargo/config.toml index 688473f2f9b..f3dd9275a42 100644 --- a/src/tools/clippy/.cargo/config.toml +++ b/src/tools/clippy/.cargo/config.toml @@ -2,9 +2,12 @@ uitest = "test --test compile-test" dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --" lintcheck = "run --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml -- " -collect-metadata = "test --test dogfood --features metadata-collector-lint -- run_metadata_collection_lint --ignored" +collect-metadata = "test --test dogfood --features internal -- run_metadata_collection_lint --ignored" [build] # -Zbinary-dep-depinfo allows us to track which rlib files to use for compiling UI tests rustflags = ["-Zunstable-options", "-Zbinary-dep-depinfo"] target-dir = "target" + +[unstable] +binary-dep-depinfo = true diff --git a/src/tools/clippy/.github/workflows/clippy.yml b/src/tools/clippy/.github/workflows/clippy.yml index 3d8c39408a9..116ae031bb7 100644 --- a/src/tools/clippy/.github/workflows/clippy.yml +++ b/src/tools/clippy/.github/workflows/clippy.yml @@ -49,17 +49,17 @@ jobs: echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV - name: Build - run: cargo build --features deny-warnings,internal-lints,metadata-collector-lint + run: cargo build --features deny-warnings,internal - name: Test - run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint + run: cargo test --features deny-warnings,internal - name: Test clippy_lints - run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint + run: cargo test --features deny-warnings,internal working-directory: clippy_lints - name: Test clippy_utils - run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint + run: cargo test --features deny-warnings,internal working-directory: clippy_utils - name: Test rustc_tools_util @@ -70,14 +70,6 @@ jobs: run: cargo test --features deny-warnings working-directory: clippy_dev - - name: Test cargo-clippy - run: ../target/debug/cargo-clippy - working-directory: clippy_workspace_tests - - - name: Test cargo-clippy --fix - run: ../target/debug/cargo-clippy clippy --fix - working-directory: clippy_workspace_tests - - name: Test clippy-driver run: bash .github/driver.sh env: diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml index 8b644aa2817..989667037c1 100644 --- a/src/tools/clippy/.github/workflows/clippy_bors.yml +++ b/src/tools/clippy/.github/workflows/clippy_bors.yml @@ -112,17 +112,22 @@ jobs: echo "$SYSROOT/bin" >> $GITHUB_PATH - name: Build - run: cargo build --features deny-warnings,internal-lints,metadata-collector-lint + run: cargo build --features deny-warnings,internal - name: Test - run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint + if: runner.os == 'Linux' + run: cargo test --features deny-warnings,internal + + - name: Test + if: runner.os != 'Linux' + run: cargo test --features deny-warnings,internal -- --skip dogfood - name: Test clippy_lints - run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint + run: cargo test --features deny-warnings,internal working-directory: clippy_lints - name: Test clippy_utils - run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint + run: cargo test --features deny-warnings,internal working-directory: clippy_utils - name: Test rustc_tools_util @@ -133,14 +138,6 @@ jobs: run: cargo test --features deny-warnings working-directory: clippy_dev - - name: Test cargo-clippy - run: ../target/debug/cargo-clippy - working-directory: clippy_workspace_tests - - - name: Test cargo-clippy --fix - run: ../target/debug/cargo-clippy clippy --fix - working-directory: clippy_workspace_tests - - name: Test clippy-driver run: bash .github/driver.sh env: diff --git a/src/tools/clippy/.gitignore b/src/tools/clippy/.gitignore index e82a0ec4765..3e50c45a9b6 100644 --- a/src/tools/clippy/.gitignore +++ b/src/tools/clippy/.gitignore @@ -19,7 +19,6 @@ out /target /clippy_lints/target /clippy_utils/target -/clippy_workspace_tests/target /clippy_dev/target /lintcheck/target /rustc_tools_util/target diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 27bac4718b6..8f4da9a3827 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -2887,6 +2887,7 @@ Released 2018-09-13 [`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions [`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison [`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison +[`borrow_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr [`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const [`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box [`box_collection`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_collection @@ -3070,6 +3071,7 @@ Released 2018-09-13 [`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion [`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn +[`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map [`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten @@ -3253,6 +3255,7 @@ Released 2018-09-13 [`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait [`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names [`single_char_add_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_add_str +[`single_char_lifetime_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_lifetime_names [`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern [`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports [`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index 8661a867758..e445889a58f 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.59" +version = "0.1.60" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -47,7 +47,9 @@ itertools = "0.10" quote = "1.0" serde = { version = "1.0", features = ["derive"] } syn = { version = "1.0", features = ["full"] } +futures = "0.3" parking_lot = "0.11.2" +tokio = { version = "1", features = ["io-util"] } [build-dependencies] rustc_tools_util = { version = "0.2", path = "rustc_tools_util" } @@ -55,8 +57,7 @@ rustc_tools_util = { version = "0.2", path = "rustc_tools_util" } [features] deny-warnings = ["clippy_lints/deny-warnings"] integration = ["tempfile"] -internal-lints = ["clippy_lints/internal-lints"] -metadata-collector-lint = ["internal-lints", "clippy_lints/metadata-collector-lint"] +internal = ["clippy_lints/internal"] [package.metadata.rust-analyzer] # This package uses #[feature(rustc_private)] diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md index 1bbd89e7822..f001a42d917 100644 --- a/src/tools/clippy/README.md +++ b/src/tools/clippy/README.md @@ -5,7 +5,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 450 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 500 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category. @@ -37,8 +37,8 @@ Table of contents: ## Usage -Below are instructions on how to use Clippy as a subcommand, compiled from source -or in Travis CI. +Below are instructions on how to use Clippy as a cargo subcommand, +in projects that do not use cargo, or in Travis CI. ### As a cargo subcommand (`cargo clippy`) @@ -98,22 +98,18 @@ If you want to run Clippy **only** on the given crate, use the `--no-deps` optio cargo clippy -p example -- --no-deps ``` -### As a rustc replacement (`clippy-driver`) +### Using `clippy-driver` -Clippy can also be used in projects that do not use cargo. To do so, you will need to replace -your `rustc` compilation commands with `clippy-driver`. For example, if your project runs: - -```terminal -rustc --edition 2018 -Cpanic=abort foo.rs -``` - -Then, to enable Clippy, you will need to call: +Clippy can also be used in projects that do not use cargo. To do so, run `clippy-driver` +with the same arguments you use for `rustc`. For example: ```terminal clippy-driver --edition 2018 -Cpanic=abort foo.rs ``` -Note that `rustc` will still run, i.e. it will still emit the output files it normally does. +Note that `clippy-driver` is designed for running Clippy only and should not be used as a general +replacement for `rustc`. `clippy-driver` may produce artifacts that are not optimized as expected, +for example. ### Travis CI diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs index 9ceadee58ea..d513a229b7e 100644 --- a/src/tools/clippy/clippy_dev/src/fmt.rs +++ b/src/tools/clippy/clippy_dev/src/fmt.rs @@ -3,7 +3,7 @@ use itertools::Itertools; use shell_escape::escape; use std::ffi::{OsStr, OsString}; use std::path::Path; -use std::process::{self, Command}; +use std::process::{self, Command, Stdio}; use std::{fs, io}; use walkdir::WalkDir; @@ -31,6 +31,7 @@ impl From<walkdir::Error> for CliError { struct FmtContext { check: bool, verbose: bool, + rustfmt_path: String, } // the "main" function of cargo dev fmt @@ -102,7 +103,23 @@ Please revert the changes to Cargo.tomls first." } } - let context = FmtContext { check, verbose }; + let output = Command::new("rustup") + .args(["which", "rustfmt"]) + .stderr(Stdio::inherit()) + .output() + .expect("error running `rustup which rustfmt`"); + if !output.status.success() { + eprintln!("`rustup which rustfmt` did not execute successfully"); + process::exit(1); + } + let mut rustfmt_path = String::from_utf8(output.stdout).expect("invalid rustfmt path"); + rustfmt_path.truncate(rustfmt_path.trim_end().len()); + + let context = FmtContext { + check, + verbose, + rustfmt_path, + }; let result = try_run(&context); let code = match result { Ok(true) => 0, @@ -141,8 +158,12 @@ fn exec( println!("{}", format_command(&program, &dir, args)); } - let child = Command::new(&program).current_dir(&dir).args(args.iter()).spawn()?; - let output = child.wait_with_output()?; + let output = Command::new(&program) + .env("RUSTFMT", &context.rustfmt_path) + .current_dir(&dir) + .args(args.iter()) + .output() + .unwrap(); let success = output.status.success(); if !context.check && !success { @@ -159,7 +180,6 @@ fn exec( fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> { let mut args = vec!["fmt", "--all"]; if context.check { - args.push("--"); args.push("--check"); } let success = exec(context, "cargo", path, &args)?; @@ -200,7 +220,7 @@ fn rustfmt(context: &FmtContext, paths: impl Iterator<Item = OsString>) -> Resul } args.extend(paths); - let success = exec(context, "rustfmt", std::env::current_dir()?, &args)?; + let success = exec(context, &context.rustfmt_path, std::env::current_dir()?, &args)?; Ok(success) } diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs index 8dd073ef405..d368ef1f46a 100644 --- a/src/tools/clippy/clippy_dev/src/update_lints.rs +++ b/src/tools/clippy/clippy_dev/src/update_lints.rs @@ -321,7 +321,7 @@ fn gen_register_lint_list<'a>( for (is_public, module_name, lint_name) in details { if !is_public { - output.push_str(" #[cfg(feature = \"internal-lints\")]\n"); + output.push_str(" #[cfg(feature = \"internal\")]\n"); } output.push_str(&format!(" {}::{},\n", module_name, lint_name)); } diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index 7d2a3e4f639..2053ca64ba2 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.59" +version = "0.1.60" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -30,8 +30,7 @@ url = { version = "2.2", features = ["serde"] } [features] deny-warnings = ["clippy_utils/deny-warnings"] # build clippy with internal lints enabled, off by default -internal-lints = ["clippy_utils/internal-lints"] -metadata-collector-lint = ["serde_json", "clippy_utils/metadata-collector-lint"] +internal = ["clippy_utils/internal", "serde_json"] [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)] 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 b7f414742f1..c82837746bd 100644 --- a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs +++ b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs @@ -1,12 +1,10 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::higher; -use clippy_utils::source::snippet_opt; -use clippy_utils::{is_direct_expn_of, is_expn_of, match_panic_call, peel_blocks}; -use if_chain::if_chain; -use rustc_hir::{Expr, ExprKind, UnOp}; +use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn}; +use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -36,107 +34,39 @@ declare_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]); impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - let lint_true = |is_debug: bool| { - span_lint_and_help( - cx, - ASSERTIONS_ON_CONSTANTS, - e.span, - if is_debug { - "`debug_assert!(true)` will be optimized out by the compiler" - } else { - "`assert!(true)` will be optimized out by the compiler" - }, - None, - "remove it", - ); + let Some(macro_call) = root_macro_call_first_node(cx, e) else { return }; + let is_debug = match cx.tcx.get_diagnostic_name(macro_call.def_id) { + Some(sym::debug_assert_macro) => true, + Some(sym::assert_macro) => false, + _ => return, }; - let lint_false_without_message = || { + let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) else { return }; + let Some((Constant::Bool(val), _)) = constant(cx, cx.typeck_results(), condition) else { return }; + if val { span_lint_and_help( cx, ASSERTIONS_ON_CONSTANTS, - e.span, - "`assert!(false)` should probably be replaced", + macro_call.span, + &format!( + "`{}!(true)` will be optimized out by the compiler", + cx.tcx.item_name(macro_call.def_id) + ), None, - "use `panic!()` or `unreachable!()`", + "remove it", ); - }; - let lint_false_with_message = |panic_message: String| { + } else if !is_debug { + let (assert_arg, panic_arg) = match panic_expn { + PanicExpn::Empty => ("", ""), + _ => (", ..", ".."), + }; span_lint_and_help( cx, ASSERTIONS_ON_CONSTANTS, - e.span, - &format!("`assert!(false, {})` should probably be replaced", panic_message), + macro_call.span, + &format!("`assert!(false{})` should probably be replaced", assert_arg), None, - &format!("use `panic!({})` or `unreachable!({})`", panic_message, panic_message), + &format!("use `panic!({})` or `unreachable!({0})`", panic_arg), ); - }; - - if let Some(debug_assert_span) = is_expn_of(e.span, "debug_assert") { - if debug_assert_span.from_expansion() { - return; - } - if_chain! { - if let ExprKind::Unary(_, lit) = e.kind; - if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), lit); - if is_true; - then { - lint_true(true); - } - }; - } else if let Some(assert_span) = is_direct_expn_of(e.span, "assert") { - if assert_span.from_expansion() { - return; - } - if let Some(assert_match) = match_assert_with_message(cx, e) { - match assert_match { - // matched assert but not message - AssertKind::WithoutMessage(false) => lint_false_without_message(), - AssertKind::WithoutMessage(true) | AssertKind::WithMessage(_, true) => lint_true(false), - AssertKind::WithMessage(panic_message, false) => lint_false_with_message(panic_message), - }; - } - } - } -} - -/// Result of calling `match_assert_with_message`. -enum AssertKind { - WithMessage(String, bool), - WithoutMessage(bool), -} - -/// Check if the expression matches -/// -/// ```rust,ignore -/// if !c { -/// { -/// ::std::rt::begin_panic(message, _) -/// } -/// } -/// ``` -/// -/// where `message` is any expression and `c` is a constant bool. -fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<AssertKind> { - if_chain! { - if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr); - if let ExprKind::Unary(UnOp::Not, expr) = cond.kind; - // bind the first argument of the `assert!` macro - if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), expr); - let begin_panic_call = peel_blocks(then); - // function call - if let Some(arg) = match_panic_call(cx, begin_panic_call); - // bind the second argument of the `assert!` macro if it exists - if let panic_message = snippet_opt(cx, arg.span); - // second argument of begin_panic is irrelevant - // as is the second match arm - then { - // an empty message occurs when it was generated by the macro - // (and not passed by the user) - return panic_message - .filter(|msg| !msg.is_empty()) - .map(|msg| AssertKind::WithMessage(msg, is_true)) - .or(Some(AssertKind::WithoutMessage(is_true))); } } - None } diff --git a/src/tools/clippy/clippy_lints/src/attrs.rs b/src/tools/clippy/clippy_lints/src/attrs.rs index 0629674307b..a58d12ddd6b 100644 --- a/src/tools/clippy/clippy_lints/src/attrs.rs +++ b/src/tools/clippy/clippy_lints/src/attrs.rs @@ -1,9 +1,10 @@ //! checks for attributes use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::macros::{is_panic, macro_backtrace}; use clippy_utils::msrvs; use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments}; -use clippy_utils::{extract_msrv_attr, match_panic_def_id, meets_msrv}; +use clippy_utils::{extract_msrv_attr, meets_msrv}; use if_chain::if_chain; use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; use rustc_errors::Applicability; @@ -443,20 +444,15 @@ fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_ } fn is_relevant_expr(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, expr: &Expr<'_>) -> bool { + if macro_backtrace(expr.span).last().map_or(false, |macro_call| { + is_panic(cx, macro_call.def_id) || cx.tcx.item_name(macro_call.def_id) == sym::unreachable + }) { + return false; + } match &expr.kind { ExprKind::Block(block, _) => is_relevant_block(cx, typeck_results, block), ExprKind::Ret(Some(e)) => is_relevant_expr(cx, typeck_results, e), ExprKind::Ret(None) | ExprKind::Break(_, None) => false, - ExprKind::Call(path_expr, _) => { - if let ExprKind::Path(qpath) = &path_expr.kind { - typeck_results - .qpath_res(qpath, path_expr.hir_id) - .opt_def_id() - .map_or(true, |fun_id| !match_panic_def_id(cx, fun_id)) - } else { - true - } - }, _ => true, } } 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 d0b8c52a36a..c50e214be28 100644 --- a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs +++ b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs @@ -1,4 +1,5 @@ -use clippy_utils::{diagnostics::span_lint_and_sugg, higher, is_direct_expn_of, ty::implements_trait}; +use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; +use clippy_utils::{diagnostics::span_lint_and_sugg, ty::implements_trait}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Lit}; @@ -41,7 +42,7 @@ fn is_bool_lit(e: &Expr<'_>) -> bool { ) && !e.span.from_expansion() } -fn is_impl_not_trait_with_bool_out(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { +fn is_impl_not_trait_with_bool_out(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(e); cx.tcx @@ -66,44 +67,40 @@ fn is_impl_not_trait_with_bool_out(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let macros = ["assert_eq", "debug_assert_eq"]; - let inverted_macros = ["assert_ne", "debug_assert_ne"]; - - for mac in macros.iter().chain(inverted_macros.iter()) { - if let Some(span) = is_direct_expn_of(expr.span, mac) { - if let Some(args) = higher::extract_assert_macro_args(expr) { - if let [a, b, ..] = args[..] { - let nb_bool_args = usize::from(is_bool_lit(a)) + usize::from(is_bool_lit(b)); - - if nb_bool_args != 1 { - // If there are two boolean arguments, we definitely don't understand - // what's going on, so better leave things as is... - // - // Or there is simply no boolean and then we can leave things as is! - return; - } - - if !is_impl_not_trait_with_bool_out(cx, a) || !is_impl_not_trait_with_bool_out(cx, b) { - // At this point the expression which is not a boolean - // literal does not implement Not trait with a bool output, - // so we cannot suggest to rewrite our code - return; - } + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; + let macro_name = cx.tcx.item_name(macro_call.def_id); + if !matches!( + macro_name.as_str(), + "assert_eq" | "debug_assert_eq" | "assert_ne" | "debug_assert_ne" + ) { + return; + } + let Some ((a, b, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return }; + if !(is_bool_lit(a) ^ is_bool_lit(b)) { + // If there are two boolean arguments, we definitely don't understand + // what's going on, so better leave things as is... + // + // Or there is simply no boolean and then we can leave things as is! + return; + } - let non_eq_mac = &mac[..mac.len() - 3]; - span_lint_and_sugg( - cx, - BOOL_ASSERT_COMPARISON, - span, - &format!("used `{}!` with a literal bool", mac), - "replace it with", - format!("{}!(..)", non_eq_mac), - Applicability::MaybeIncorrect, - ); - return; - } - } - } + if !is_impl_not_trait_with_bool_out(cx, a) || !is_impl_not_trait_with_bool_out(cx, b) { + // At this point the expression which is not a boolean + // literal does not implement Not trait with a bool output, + // so we cannot suggest to rewrite our code + return; } + + let macro_name = macro_name.as_str(); + let non_eq_mac = ¯o_name[..macro_name.len() - 3]; + span_lint_and_sugg( + cx, + BOOL_ASSERT_COMPARISON, + macro_call.span, + &format!("used `{}!` with a literal bool", macro_name), + "replace it with", + format!("{}!(..)", non_eq_mac), + Applicability::MaybeIncorrect, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/borrow_as_ptr.rs b/src/tools/clippy/clippy_lints/src/borrow_as_ptr.rs new file mode 100644 index 00000000000..b8f5217af2b --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/borrow_as_ptr.rs @@ -0,0 +1,97 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_no_std_crate; +use clippy_utils::source::snippet_opt; +use clippy_utils::{meets_msrv, msrvs}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, TyKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for the usage of `&expr as *const T` or + /// `&mut expr as *mut T`, and suggest using `ptr::addr_of` or + /// `ptr::addr_of_mut` instead. + /// + /// ### Why is this bad? + /// This would improve readability and avoid creating a reference + /// that points to an uninitialized value or unaligned place. + /// Read the `ptr::addr_of` docs for more information. + /// + /// ### Example + /// ```rust + /// let val = 1; + /// let p = &val as *const i32; + /// + /// let mut val_mut = 1; + /// let p_mut = &mut val_mut as *mut i32; + /// ``` + /// Use instead: + /// ```rust + /// let val = 1; + /// let p = std::ptr::addr_of!(val); + /// + /// let mut val_mut = 1; + /// let p_mut = std::ptr::addr_of_mut!(val_mut); + /// ``` + #[clippy::version = "1.60.0"] + pub BORROW_AS_PTR, + pedantic, + "borrowing just to cast to a raw pointer" +} + +impl_lint_pass!(BorrowAsPtr => [BORROW_AS_PTR]); + +pub struct BorrowAsPtr { + msrv: Option<RustcVersion>, +} + +impl BorrowAsPtr { + #[must_use] + pub fn new(msrv: Option<RustcVersion>) -> Self { + Self { msrv } + } +} + +impl<'tcx> LateLintPass<'tcx> for BorrowAsPtr { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if !meets_msrv(self.msrv.as_ref(), &msrvs::BORROW_AS_PTR) { + return; + } + + if expr.span.from_expansion() { + return; + } + + if_chain! { + if let ExprKind::Cast(left_expr, ty) = &expr.kind; + if let TyKind::Ptr(_) = ty.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = &left_expr.kind; + + then { + let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" }; + let macro_name = match mutability { + Mutability::Not => "addr_of", + Mutability::Mut => "addr_of_mut", + }; + + span_lint_and_sugg( + cx, + BORROW_AS_PTR, + expr.span, + "borrow as raw pointer", + "try", + format!( + "{}::ptr::{}!({})", + core_or_std, + macro_name, + snippet_opt(cx, e.span).unwrap() + ), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/case_sensitive_file_extension_comparisons.rs b/src/tools/clippy/clippy_lints/src/case_sensitive_file_extension_comparisons.rs index 3f286dd9e2f..e8f39cd3709 100644 --- a/src/tools/clippy/clippy_lints/src/case_sensitive_file_extension_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/case_sensitive_file_extension_comparisons.rs @@ -67,7 +67,7 @@ fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: & None } -impl LateLintPass<'tcx> for CaseSensitiveFileExtensionComparisons { +impl<'tcx> LateLintPass<'tcx> for CaseSensitiveFileExtensionComparisons { fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let Some(span) = check_case_sensitive_file_extension_comparison(ctx, expr) { span_lint_and_help( 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 248b35b024e..b9de5510455 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 @@ -9,7 +9,7 @@ use rustc_span::symbol::sym; use super::CAST_PTR_ALIGNMENT; -pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::Cast(cast_expr, cast_to) = expr.kind { if is_hir_ty_cfg_dependant(cx, cast_to) { return; diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_ref_to_mut.rs b/src/tools/clippy/clippy_lints/src/casts/cast_ref_to_mut.rs index d9bf1ea58b9..15f2f81f407 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_ref_to_mut.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_ref_to_mut.rs @@ -6,7 +6,7 @@ use rustc_middle::ty; use super::CAST_REF_TO_MUT; -pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if let ExprKind::Unary(UnOp::Deref, e) = &expr.kind; if let ExprKind::Cast(e, t) = &e.kind; diff --git a/src/tools/clippy/clippy_lints/src/casts/char_lit_as_u8.rs b/src/tools/clippy/clippy_lints/src/casts/char_lit_as_u8.rs index 099a0de881f..7cc406018db 100644 --- a/src/tools/clippy/clippy_lints/src/casts/char_lit_as_u8.rs +++ b/src/tools/clippy/clippy_lints/src/casts/char_lit_as_u8.rs @@ -9,7 +9,7 @@ use rustc_middle::ty::{self, UintTy}; use super::CHAR_LIT_AS_U8; -pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if let ExprKind::Cast(e, _) = &expr.kind; if let ExprKind::Lit(l) = &e.kind; 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 3132d3a5cf0..fb04f93fbcf 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 @@ -12,7 +12,7 @@ use rustc_semver::RustcVersion; use super::PTR_AS_PTR; -pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: &Option<RustcVersion>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option<RustcVersion>) { if !meets_msrv(msrv.as_ref(), &msrvs::POINTER_CAST) { return; } diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs index d07bc23235b..73ce656ad15 100644 --- a/src/tools/clippy/clippy_lints/src/copies.rs +++ b/src/tools/clippy/clippy_lints/src/copies.rs @@ -316,7 +316,7 @@ struct BlockEqual { /// This function can also trigger the `IF_SAME_THEN_ELSE` in which case it'll return `None` to /// abort any further processing and avoid duplicate lint triggers. -fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option<BlockEqual> { +fn scan_block_for_eq(cx: &LateContext<'_>, blocks: &[&Block<'_>]) -> Option<BlockEqual> { let mut start_eq = usize::MAX; let mut end_eq = usize::MAX; let mut expr_eq = true; @@ -385,11 +385,7 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option< }) } -fn check_for_warn_of_moved_symbol( - cx: &LateContext<'tcx>, - symbols: &FxHashSet<Symbol>, - if_expr: &'tcx Expr<'_>, -) -> bool { +fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &FxHashSet<Symbol>, if_expr: &Expr<'_>) -> bool { get_enclosing_block(cx, if_expr.hir_id).map_or(false, |block| { let ignore_span = block.span.shrink_to_lo().to(if_expr.span); @@ -419,13 +415,13 @@ fn check_for_warn_of_moved_symbol( } fn emit_branches_sharing_code_lint( - cx: &LateContext<'tcx>, + cx: &LateContext<'_>, start_stmts: usize, end_stmts: usize, lint_end: bool, warn_about_moved_symbol: bool, - blocks: &[&Block<'tcx>], - if_expr: &'tcx Expr<'_>, + blocks: &[&Block<'_>], + if_expr: &Expr<'_>, ) { if start_stmts == 0 && !lint_end { return; diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs index a0b137efe22..3070588483c 100644 --- a/src/tools/clippy/clippy_lints/src/default.rs +++ b/src/tools/clippy/clippy_lints/src/default.rs @@ -77,7 +77,7 @@ pub struct Default { impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]); -impl LateLintPass<'_> for Default { +impl<'tcx> LateLintPass<'tcx> for Default { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { if !expr.span.from_expansion(); @@ -110,7 +110,7 @@ impl LateLintPass<'_> for Default { } #[allow(clippy::too_many_lines)] - fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) { // start from the `let mut _ = _::default();` and look at all the following // statements, see if they re-assign the fields of the binding let stmts_head = match block.stmts { @@ -198,7 +198,7 @@ impl LateLintPass<'_> for Default { let ext_with_default = !variant .fields .iter() - .all(|field| assigned_fields.iter().any(|(a, _)| a == &field.ident.name)); + .all(|field| assigned_fields.iter().any(|(a, _)| a == &field.name)); let field_list = assigned_fields .into_iter() diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs index 3573ea5f026..66b5f49817d 100644 --- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs +++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs @@ -54,7 +54,7 @@ declare_clippy_lint! { declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]); -impl LateLintPass<'_> for DefaultNumericFallback { +impl<'tcx> LateLintPass<'tcx> for DefaultNumericFallback { fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { let mut visitor = NumericFallbackVisitor::new(cx); visitor.visit_body(body); @@ -161,7 +161,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { fields_def .iter() .find_map(|f_def| { - if f_def.ident == field.ident + if f_def.ident(self.cx.tcx) == field.ident { Some(self.cx.tcx.type_of(f_def.did)) } else { None } }); diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index fa2b348591b..bf077a212fd 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -355,7 +355,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { } } -fn try_parse_ref_op( +fn try_parse_ref_op<'tcx>( tcx: TyCtxt<'tcx>, typeck: &'tcx TypeckResults<'_>, expr: &'tcx Expr<'_>, @@ -387,7 +387,7 @@ fn try_parse_ref_op( // Checks whether the type for a deref call actually changed the type, not just the mutability of // the reference. -fn deref_method_same_type(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool { +fn deref_method_same_type(result_ty: Ty<'_>, arg_ty: Ty<'_>) -> bool { match (result_ty.kind(), arg_ty.kind()) { (ty::Ref(_, result_ty, _), ty::Ref(_, arg_ty, _)) => TyS::same_type(result_ty, arg_ty), @@ -457,7 +457,7 @@ fn is_linted_explicit_deref_position(parent: Option<Node<'_>>, child_id: HirId, } /// Adjustments are sometimes made in the parent block rather than the expression itself. -fn find_adjustments( +fn find_adjustments<'tcx>( tcx: TyCtxt<'tcx>, typeck: &'tcx TypeckResults<'_>, expr: &'tcx Expr<'_>, @@ -499,7 +499,7 @@ fn find_adjustments( } #[allow(clippy::needless_pass_by_value)] -fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) { +fn report(cx: &LateContext<'_>, expr: &Expr<'_>, state: State, data: StateData) { match state { State::DerefMethod { ty_changed_count, @@ -568,7 +568,7 @@ fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: Stat } impl Dereferencing { - fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, local: HirId) { + fn check_local_usage(&mut self, cx: &LateContext<'_>, e: &Expr<'_>, local: HirId) { if let Some(outer_pat) = self.ref_locals.get_mut(&local) { if let Some(pat) = outer_pat { // Check for auto-deref diff --git a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs index 6d4065907fb..73c00d97020 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs @@ -11,6 +11,9 @@ declare_clippy_lint! { /// ### What it does /// Denies the configured methods and functions in clippy.toml /// + /// Note: Even though this lint is warn-by-default, it will only trigger if + /// methods are defined in the clippy.toml file. + /// /// ### Why is this bad? /// Some methods are undesirable in certain contexts, and it's beneficial to /// lint for them as needed. @@ -49,14 +52,14 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.49.0"] pub DISALLOWED_METHODS, - nursery, + style, "use of a disallowed method call" } #[derive(Clone, Debug)] pub struct DisallowedMethods { conf_disallowed: Vec<conf::DisallowedMethod>, - disallowed: DefIdMap<Option<String>>, + disallowed: DefIdMap<usize>, } impl DisallowedMethods { @@ -72,17 +75,10 @@ impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]); impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { fn check_crate(&mut self, cx: &LateContext<'_>) { - for conf in &self.conf_disallowed { - let (path, reason) = match conf { - conf::DisallowedMethod::Simple(path) => (path, None), - conf::DisallowedMethod::WithReason { path, reason } => ( - path, - reason.as_ref().map(|reason| format!("{} (from clippy.toml)", reason)), - ), - }; - let segs: Vec<_> = path.split("::").collect(); + for (index, conf) in self.conf_disallowed.iter().enumerate() { + let segs: Vec<_> = conf.path().split("::").collect(); if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs) { - self.disallowed.insert(id, reason); + self.disallowed.insert(id, index); } } } @@ -92,15 +88,17 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { Some(def_id) => def_id, None => return, }; - let reason = match self.disallowed.get(&def_id) { - Some(reason) => reason, + let conf = match self.disallowed.get(&def_id) { + Some(&index) => &self.conf_disallowed[index], None => return, }; - let func_path = cx.tcx.def_path_str(def_id); - let msg = format!("use of a disallowed method `{}`", func_path); + let msg = format!("use of a disallowed method `{}`", conf.path()); span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, &msg, |diag| { - if let Some(reason) = reason { - diag.note(reason); + if let conf::DisallowedMethod::WithReason { + reason: Some(reason), .. + } = conf + { + diag.note(&format!("{} (from clippy.toml)", reason)); } }); } diff --git a/src/tools/clippy/clippy_lints/src/disallowed_types.rs b/src/tools/clippy/clippy_lints/src/disallowed_types.rs index eaed4032713..ea4b49b46fe 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_types.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_types.rs @@ -14,6 +14,9 @@ declare_clippy_lint! { /// ### What it does /// Denies the configured types in clippy.toml. /// + /// Note: Even though this lint is warn-by-default, it will only trigger if + /// types are defined in the clippy.toml file. + /// /// ### Why is this bad? /// Some types are undesirable in certain contexts. /// @@ -44,7 +47,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.55.0"] pub DISALLOWED_TYPES, - nursery, + style, "use of disallowed types" } #[derive(Clone, Debug)] diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs index 7c271773357..cb7d5ac7394 100644 --- a/src/tools/clippy/clippy_lints/src/doc.rs +++ b/src/tools/clippy/clippy_lints/src/doc.rs @@ -1,8 +1,9 @@ use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_then}; +use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::source::{first_line_of_span, snippet_with_applicability}; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use clippy_utils::{is_entrypoint_fn, is_expn_of, match_panic_def_id, method_chain_args, return_ty}; +use clippy_utils::{is_entrypoint_fn, method_chain_args, return_ty}; use if_chain::if_chain; use itertools::Itertools; use rustc_ast::ast::{Async, AttrKind, Attribute, Fn, FnRetTy, ItemKind}; @@ -13,7 +14,7 @@ use rustc_errors::emitter::EmitterWriter; use rustc_errors::{Applicability, Handler, SuggestionStyle}; use rustc_hir as hir; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::{AnonConst, Expr, ExprKind, QPath}; +use rustc_hir::{AnonConst, Expr}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; @@ -805,24 +806,17 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { return; } - // check for `begin_panic` - if_chain! { - if let ExprKind::Call(func_expr, _) = expr.kind; - if let ExprKind::Path(QPath::Resolved(_, path)) = func_expr.kind; - if let Some(path_def_id) = path.res.opt_def_id(); - if match_panic_def_id(self.cx, path_def_id); - if is_expn_of(expr.span, "unreachable").is_none(); - if !is_expn_of_debug_assertions(expr.span); - then { - self.panic_span = Some(expr.span); + if let Some(macro_call) = root_macro_call_first_node(self.cx, expr) { + if is_panic(self.cx, macro_call.def_id) + || matches!( + self.cx.tcx.item_name(macro_call.def_id).as_str(), + "assert" | "assert_eq" | "assert_ne" | "todo" + ) + { + self.panic_span = Some(macro_call.span); } } - // check for `assert_eq` or `assert_ne` - if is_expn_of(expr.span, "assert_eq").is_some() || is_expn_of(expr.span, "assert_ne").is_some() { - self.panic_span = Some(expr.span); - } - // check for `unwrap` if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { let receiver_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs(); @@ -844,8 +838,3 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) } } - -fn is_expn_of_debug_assertions(span: Span) -> bool { - const MACRO_NAMES: &[&str] = &["debug_assert", "debug_assert_eq", "debug_assert_ne"]; - MACRO_NAMES.iter().any(|name| is_expn_of(span, name).is_some()) -} diff --git a/src/tools/clippy/clippy_lints/src/entry.rs b/src/tools/clippy/clippy_lints/src/entry.rs index 3d92eb16870..3ce239273e2 100644 --- a/src/tools/clippy/clippy_lints/src/entry.rs +++ b/src/tools/clippy/clippy_lints/src/entry.rs @@ -233,7 +233,7 @@ struct ContainsExpr<'tcx> { key: &'tcx Expr<'tcx>, call_ctxt: SyntaxContext, } -fn try_parse_contains(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(MapType, ContainsExpr<'tcx>)> { +fn try_parse_contains<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(MapType, ContainsExpr<'tcx>)> { let mut negated = false; let expr = peel_hir_expr_while(expr, |e| match e.kind { ExprKind::Unary(UnOp::Not, e) => { @@ -280,7 +280,7 @@ struct InsertExpr<'tcx> { key: &'tcx Expr<'tcx>, value: &'tcx Expr<'tcx>, } -fn try_parse_insert(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<InsertExpr<'tcx>> { +fn try_parse_insert<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<InsertExpr<'tcx>> { if let ExprKind::MethodCall(_, _, [map, key, value], _) = expr.kind { let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?; if match_def_path(cx, id, &paths::BTREEMAP_INSERT) || match_def_path(cx, id, &paths::HASHMAP_INSERT) { @@ -301,7 +301,7 @@ enum Edit<'tcx> { /// An insertion into the map. Insertion(Insertion<'tcx>), } -impl Edit<'tcx> { +impl<'tcx> Edit<'tcx> { fn as_insertion(self) -> Option<Insertion<'tcx>> { if let Self::Insertion(i) = self { Some(i) } else { None } } @@ -532,7 +532,7 @@ struct InsertSearchResults<'tcx> { allow_insert_closure: bool, is_single_insert: bool, } -impl InsertSearchResults<'tcx> { +impl<'tcx> InsertSearchResults<'tcx> { fn as_single_insertion(&self) -> Option<Insertion<'tcx>> { self.is_single_insert.then(|| self.edits[0].as_insertion().unwrap()) } @@ -633,7 +633,7 @@ impl InsertSearchResults<'tcx> { } } -fn find_insert_calls( +fn find_insert_calls<'tcx>( cx: &LateContext<'tcx>, contains_expr: &ContainsExpr<'tcx>, expr: &'tcx Expr<'_>, diff --git a/src/tools/clippy/clippy_lints/src/eq_op.rs b/src/tools/clippy/clippy_lints/src/eq_op.rs index 10123460527..df75b815436 100644 --- a/src/tools/clippy/clippy_lints/src/eq_op.rs +++ b/src/tools/clippy/clippy_lints/src/eq_op.rs @@ -1,10 +1,11 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_then}; +use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace}; use clippy_utils::source::snippet; use clippy_utils::ty::{implements_trait, is_copy}; -use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, is_expn_of, is_in_test_function}; +use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, is_in_test_function}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, StmtKind}; +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -68,32 +69,26 @@ declare_clippy_lint! { declare_lint_pass!(EqOp => [EQ_OP, OP_REF]); -const ASSERT_MACRO_NAMES: [&str; 4] = ["assert_eq", "assert_ne", "debug_assert_eq", "debug_assert_ne"]; - impl<'tcx> LateLintPass<'tcx> for EqOp { #[allow(clippy::similar_names, clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let ExprKind::Block(block, _) = e.kind { - for stmt in block.stmts { - for amn in &ASSERT_MACRO_NAMES { - if_chain! { - if is_expn_of(stmt.span, amn).is_some(); - if let StmtKind::Semi(matchexpr) = stmt.kind; - if let Some(macro_args) = higher::extract_assert_macro_args(matchexpr); - if macro_args.len() == 2; - let (lhs, rhs) = (macro_args[0], macro_args[1]); - if eq_expr_value(cx, lhs, rhs); - if !is_in_test_function(cx.tcx, e.hir_id); - then { - span_lint( - cx, - EQ_OP, - lhs.span.to(rhs.span), - &format!("identical args used in this `{}!` macro call", amn), - ); - } - } - } + if_chain! { + if let Some((macro_call, macro_name)) = first_node_macro_backtrace(cx, e).find_map(|macro_call| { + let name = cx.tcx.item_name(macro_call.def_id); + matches!(name.as_str(), "assert_eq" | "assert_ne" | "debug_assert_eq" | "debug_assert_ne") + .then(|| (macro_call, name)) + }); + if let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn); + if eq_expr_value(cx, lhs, rhs); + if macro_call.is_local(); + if !is_in_test_function(cx.tcx, e.hir_id); + then { + span_lint( + cx, + EQ_OP, + lhs.span.to(rhs.span), + &format!("identical args used in this `{}!` macro call", macro_name), + ); } } if let ExprKind::Binary(op, left, right) = e.kind { diff --git a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs index 06d128f5527..cf47e581ccb 100644 --- a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs +++ b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs @@ -56,7 +56,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool { } } -fn is_structural_partial_eq(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> bool { +fn is_structural_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> bool { if let Some(def_id) = cx.tcx.lang_items().eq_trait() { implements_trait(cx, ty, def_id, &[other.into()]) } else { diff --git a/src/tools/clippy/clippy_lints/src/erasing_op.rs b/src/tools/clippy/clippy_lints/src/erasing_op.rs index d49cec26be5..c1a84973c42 100644 --- a/src/tools/clippy/clippy_lints/src/erasing_op.rs +++ b/src/tools/clippy/clippy_lints/src/erasing_op.rs @@ -1,9 +1,11 @@ use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::diagnostics::span_lint; +use clippy_utils::ty::same_type_and_consts; + use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TypeckResults; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; declare_clippy_lint! { /// ### What it does @@ -35,24 +37,40 @@ impl<'tcx> LateLintPass<'tcx> for ErasingOp { return; } if let ExprKind::Binary(ref cmp, left, right) = e.kind { + let tck = cx.typeck_results(); match cmp.node { BinOpKind::Mul | BinOpKind::BitAnd => { - check(cx, left, e.span); - check(cx, right, e.span); + check(cx, tck, left, right, e); + check(cx, tck, right, left, e); }, - BinOpKind::Div => check(cx, left, e.span), + BinOpKind::Div => check(cx, tck, left, right, e), _ => (), } } } } -fn check(cx: &LateContext<'_>, e: &Expr<'_>, span: Span) { - if constant_simple(cx, cx.typeck_results(), e) == Some(Constant::Int(0)) { +fn different_types(tck: &TypeckResults<'_>, input: &Expr<'_>, output: &Expr<'_>) -> bool { + let input_ty = tck.expr_ty(input).peel_refs(); + let output_ty = tck.expr_ty(output).peel_refs(); + !same_type_and_consts(input_ty, output_ty) +} + +fn check<'tcx>( + cx: &LateContext<'tcx>, + tck: &TypeckResults<'tcx>, + op: &Expr<'tcx>, + other: &Expr<'tcx>, + parent: &Expr<'tcx>, +) { + if constant_simple(cx, tck, op) == Some(Constant::Int(0)) { + if different_types(tck, other, parent) { + return; + } span_lint( cx, ERASING_OP, - span, + parent.span, "this operation will always return zero. This is likely not the intended outcome", ); } diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 5a4b4247104..b22515a3907 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet_opt; +use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::local_used_after_expr; use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local, path_to_local_id}; use if_chain::if_chain; @@ -12,6 +13,7 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, ClosureKind, Ty, TypeFoldable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does @@ -113,6 +115,9 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { // A type param function ref like `T::f` is not 'static, however // it is if cast like `T::f as fn()`. This seems like a rustc bug. if !substs.types().any(|t| matches!(t.kind(), ty::Param(_))); + let callee_ty_unadjusted = cx.typeck_results().expr_ty(callee).peel_refs(); + if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Arc); + if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Rc); then { span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| { if let Some(mut snippet) = snippet_opt(cx, callee.span) { diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs index 6b327b9ce17..98e5234e0aa 100644 --- a/src/tools/clippy/clippy_lints/src/explicit_write.rs +++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; -use clippy_utils::higher::FormatArgsExpn; +use clippy_utils::macros::FormatArgsExpn; use clippy_utils::{is_expn_of, match_function_call, paths}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -48,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { } else { None }; - if let Some(format_args) = FormatArgsExpn::parse(write_arg); + if let Some(format_args) = FormatArgsExpn::parse(cx, write_arg); then { let calling_macro = // ordering is important here, since `writeln!` uses `write!` internally @@ -80,7 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { ) }; let msg = format!("use of `{}.unwrap()`", used); - if let [write_output] = *format_args.format_string_symbols { + if let [write_output] = *format_args.format_string_parts { let mut write_output = write_output.to_string(); if write_output.ends_with('\n') { write_output.pop(); diff --git a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs index 05d300058cf..02f1baf27fa 100644 --- a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs +++ b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::{is_panic, root_macro_call_first_node}; +use clippy_utils::method_chain_args; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_expn_of, match_panic_def_id, method_chain_args}; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -68,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for FallibleImplFrom { fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[hir::ImplItemRef]) { use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; - use rustc_hir::{Expr, ExprKind, ImplItemKind, QPath}; + use rustc_hir::{Expr, ImplItemKind}; struct FindPanicUnwrap<'a, 'tcx> { lcx: &'a LateContext<'tcx>, @@ -80,14 +81,8 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[h type Map = Map<'tcx>; fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - // check for `begin_panic` - if_chain! { - if let ExprKind::Call(func_expr, _) = expr.kind; - if let ExprKind::Path(QPath::Resolved(_, path)) = func_expr.kind; - if let Some(path_def_id) = path.res.opt_def_id(); - if match_panic_def_id(self.lcx, path_def_id); - if is_expn_of(expr.span, "unreachable").is_none(); - then { + if let Some(macro_call) = root_macro_call_first_node(self.lcx, expr) { + if is_panic(self.lcx, macro_call.def_id) { self.result.push(expr.span); } } diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs index 3f043e5f2f1..688d8f8630f 100644 --- a/src/tools/clippy/clippy_lints/src/format.rs +++ b/src/tools/clippy/clippy_lints/src/format.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::higher::FormatExpn; +use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn}; use clippy_utils::source::{snippet_opt, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use if_chain::if_chain; @@ -43,38 +43,41 @@ declare_lint_pass!(UselessFormat => [USELESS_FORMAT]); impl<'tcx> LateLintPass<'tcx> for UselessFormat { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let FormatExpn { call_site, format_args } = match FormatExpn::parse(expr) { - Some(e) if !e.call_site.from_expansion() => e, - _ => return, + let (format_args, call_site) = if_chain! { + if let Some(macro_call) = root_macro_call_first_node(cx, expr); + if cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id); + if let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, macro_call.expn); + then { + (format_args, macro_call.span) + } else { + return + } }; let mut applicability = Applicability::MachineApplicable; if format_args.value_args.is_empty() { - if format_args.format_string_parts.is_empty() { - span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability); - } else { - if_chain! { - if let [e] = &*format_args.format_string_parts; - if let ExprKind::Lit(lit) = &e.kind; - if let Some(s_src) = snippet_opt(cx, lit.span); - then { + match *format_args.format_string_parts { + [] => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability), + [_] => { + if let Some(s_src) = snippet_opt(cx, format_args.format_string_span) { // Simulate macro expansion, converting {{ and }} to { and }. let s_expand = s_src.replace("{{", "{").replace("}}", "}"); let sugg = format!("{}.to_string()", s_expand); span_useless_format(cx, call_site, sugg, applicability); } - } + }, + [..] => {}, } } else if let [value] = *format_args.value_args { if_chain! { - if format_args.format_string_symbols == [kw::Empty]; + if format_args.format_string_parts == [kw::Empty]; if match cx.typeck_results().expr_ty(value).peel_refs().kind() { ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did), ty::Str => true, _ => false, }; if let Some(args) = format_args.args(); - if args.iter().all(|arg| arg.is_display() && !arg.has_string_formatting()); + if args.iter().all(|arg| arg.format_trait == sym::Display && !arg.has_string_formatting()); then { let is_new_string = match value.kind { ExprKind::Binary(..) => true, diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs index f0e1a67dcdd..ae423d799d7 100644 --- a/src/tools/clippy/clippy_lints/src/format_args.rs +++ b/src/tools/clippy/clippy_lints/src/format_args.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::higher::{FormatArgsArg, FormatArgsExpn, FormatExpn}; +use clippy_utils::macros::{FormatArgsArg, FormatArgsExpn}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::implements_trait; use clippy_utils::{is_diag_trait_item, match_def_path, paths}; @@ -83,7 +83,7 @@ const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[sym::format_macro, sym::std_panic_m impl<'tcx> LateLintPass<'tcx> for FormatArgs { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if_chain! { - if let Some(format_args) = FormatArgsExpn::parse(expr); + if let Some(format_args) = FormatArgsExpn::parse(cx, expr); let expr_expn_data = expr.span.ctxt().outer_expn_data(); let outermost_expn_data = outermost_expn_data(expr_expn_data); if let Some(macro_def_id) = outermost_expn_data.macro_def_id; @@ -97,7 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { if let Some(args) = format_args.args(); then { for (i, arg) in args.iter().enumerate() { - if !arg.is_display() { + if arg.format_trait != sym::Display { continue; } if arg.has_string_formatting() { @@ -106,8 +106,8 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { if is_aliased(&args, i) { continue; } - check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg); - check_to_string_in_format_args(cx, name, arg); + check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.value); + check_to_string_in_format_args(cx, name, arg.value); } } } @@ -122,30 +122,31 @@ fn outermost_expn_data(expn_data: ExpnData) -> ExpnData { } } -fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symbol, arg: &FormatArgsArg<'_>) { - if_chain! { - if FormatExpn::parse(arg.value).is_some(); - if !arg.value.span.ctxt().outer_expn_data().call_site.from_expansion(); - then { - span_lint_and_then( - cx, - FORMAT_IN_FORMAT_ARGS, - call_site, - &format!("`format!` in `{}!` args", name), - |diag| { - diag.help(&format!( - "combine the `format!(..)` arguments with the outer `{}!(..)` call", - name - )); - diag.help("or consider changing `format!` to `format_args!`"); - }, - ); - } +fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symbol, arg: &Expr<'_>) { + let expn_data = arg.span.ctxt().outer_expn_data(); + if expn_data.call_site.from_expansion() { + return; + } + let Some(mac_id) = expn_data.macro_def_id else { return }; + if !cx.tcx.is_diagnostic_item(sym::format_macro, mac_id) { + return; } + span_lint_and_then( + cx, + FORMAT_IN_FORMAT_ARGS, + call_site, + &format!("`format!` in `{}!` args", name), + |diag| { + diag.help(&format!( + "combine the `format!(..)` arguments with the outer `{}!(..)` call", + name + )); + diag.help("or consider changing `format!` to `format_args!`"); + }, + ); } -fn check_to_string_in_format_args<'tcx>(cx: &LateContext<'tcx>, name: Symbol, arg: &FormatArgsArg<'tcx>) { - let value = arg.value; +fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Expr<'_>) { if_chain! { if !value.span.from_expansion(); if let ExprKind::MethodCall(_, _, [receiver], _) = value.kind; diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs index 866ff216f84..5ece2cc5ac4 100644 --- a/src/tools/clippy/clippy_lints/src/from_over_into.rs +++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs @@ -53,7 +53,7 @@ impl FromOverInto { impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]); -impl LateLintPass<'_> for FromOverInto { +impl<'tcx> LateLintPass<'tcx> for FromOverInto { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { if !meets_msrv(self.msrv.as_ref(), &msrvs::RE_REBALANCING_COHERENCE) { return; diff --git a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs index 73e800073b0..57b07513205 100644 --- a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs +++ b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs @@ -43,7 +43,7 @@ declare_clippy_lint! { declare_lint_pass!(FromStrRadix10 => [FROM_STR_RADIX_10]); -impl LateLintPass<'tcx> for FromStrRadix10 { +impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { fn check_expr(&mut self, cx: &LateContext<'tcx>, exp: &Expr<'tcx>) { if_chain! { if let ExprKind::Call(maybe_path, arguments) = &exp.kind; 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 77d08081c07..f2b4aefaead 100644 --- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs +++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs @@ -18,7 +18,7 @@ use clippy_utils::{match_def_path, must_use_attr, return_ty, trait_ref_of_method use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT}; -pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { +pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); let attr = must_use_attr(attrs); if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind { @@ -40,7 +40,7 @@ pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { } } -pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { +pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind { let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); @@ -62,7 +62,7 @@ pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem< } } -pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { +pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind { let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); diff --git a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index f83789bb219..6d829a18b2e 100644 --- a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -9,7 +9,7 @@ use clippy_utils::{iter_input_pats, path_to_local}; use super::NOT_UNSAFE_PTR_ARG_DEREF; -pub(super) fn check_fn( +pub(super) fn check_fn<'tcx>( cx: &LateContext<'tcx>, kind: intravisit::FnKind<'tcx>, decl: &'tcx hir::FnDecl<'tcx>, @@ -25,14 +25,14 @@ pub(super) fn check_fn( check_raw_ptr(cx, unsafety, decl, body, cx.tcx.hir().local_def_id(hir_id)); } -pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { +pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { if let hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(eid)) = item.kind { let body = cx.tcx.hir().body(eid); check_raw_ptr(cx, sig.header.unsafety, sig.decl, body, item.def_id); } } -fn check_raw_ptr( +fn check_raw_ptr<'tcx>( cx: &LateContext<'tcx>, unsafety: hir::Unsafety, decl: &'tcx hir::FnDecl<'tcx>, diff --git a/src/tools/clippy/clippy_lints/src/functions/result_unit_err.rs b/src/tools/clippy/clippy_lints/src/functions/result_unit_err.rs index 71f6f87ae60..73f08a04989 100644 --- a/src/tools/clippy/clippy_lints/src/functions/result_unit_err.rs +++ b/src/tools/clippy/clippy_lints/src/functions/result_unit_err.rs @@ -13,7 +13,7 @@ use clippy_utils::ty::is_type_diagnostic_item; use super::RESULT_UNIT_ERR; -pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { +pub(super) fn check_item(cx: &LateContext<'_>, item: &hir::Item<'_>) { if let hir::ItemKind::Fn(ref sig, ref _generics, _) = item.kind { let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); @@ -23,7 +23,7 @@ pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { } } -pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { +pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &hir::ImplItem<'_>) { if let hir::ImplItemKind::Fn(ref sig, _) = item.kind { let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); @@ -33,7 +33,7 @@ pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem< } } -pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { +pub(super) fn check_trait_item(cx: &LateContext<'_>, item: &hir::TraitItem<'_>) { if let hir::TraitItemKind::Fn(ref sig, _) = item.kind { let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); 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 63a14d8d4cd..3af960491ed 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 @@ -9,9 +9,9 @@ use clippy_utils::is_trait_impl_item; use super::TOO_MANY_ARGUMENTS; pub(super) fn check_fn( - cx: &LateContext<'tcx>, - kind: intravisit::FnKind<'tcx>, - decl: &'tcx hir::FnDecl<'_>, + cx: &LateContext<'_>, + kind: intravisit::FnKind<'_>, + decl: &hir::FnDecl<'_>, span: Span, hir_id: hir::HirId, too_many_arguments_threshold: u64, @@ -39,11 +39,7 @@ pub(super) fn check_fn( } } -pub(super) fn check_trait_item( - cx: &LateContext<'tcx>, - item: &'tcx hir::TraitItem<'_>, - too_many_arguments_threshold: u64, -) { +pub(super) fn check_trait_item(cx: &LateContext<'_>, item: &hir::TraitItem<'_>, too_many_arguments_threshold: u64) { if let hir::TraitItemKind::Fn(ref sig, _) = item.kind { // don't lint extern functions decls, it's not their fault if sig.header.abi == Abi::Rust { 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 65efbbab41a..54bdea7ea25 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 @@ -11,9 +11,9 @@ use super::TOO_MANY_LINES; pub(super) fn check_fn( cx: &LateContext<'_>, - kind: FnKind<'tcx>, + kind: FnKind<'_>, span: Span, - body: &'tcx hir::Body<'_>, + body: &hir::Body<'_>, too_many_lines_threshold: u64, ) { // Closures must be contained in a parent body, which will be checked for `too_many_lines`. 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 16e5c5ca603..9525c163ece 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 @@ -55,7 +55,7 @@ impl IfThenSomeElseNone { impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]); -impl LateLintPass<'_> for IfThenSomeElseNone { +impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) { if !meets_msrv(self.msrv.as_ref(), &msrvs::BOOL_THEN) { return; diff --git a/src/tools/clippy/clippy_lints/src/implicit_return.rs b/src/tools/clippy/clippy_lints/src/implicit_return.rs index 07caeada80d..d650d6e9a85 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_return.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_return.rs @@ -94,8 +94,8 @@ fn get_call_site(span: Span, ctxt: SyntaxContext) -> Option<Span> { } fn lint_implicit_returns( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, + cx: &LateContext<'_>, + expr: &Expr<'_>, // The context of the function body. ctxt: SyntaxContext, // Whether the expression is from a macro expansion. diff --git a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs index 1debdef9d86..3d44a669d8f 100644 --- a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs @@ -63,7 +63,7 @@ declare_clippy_lint! { declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRUCTOR]); -impl LateLintPass<'_> for InconsistentStructConstructor { +impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if_chain! { if !expr.span.from_expansion(); @@ -76,7 +76,7 @@ impl LateLintPass<'_> for InconsistentStructConstructor { then { let mut def_order_map = FxHashMap::default(); for (idx, field) in variant.fields.iter().enumerate() { - def_order_map.insert(field.ident.name, idx); + def_order_map.insert(field.name, idx); } if is_consistent_order(fields, &def_order_map) { 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 69f1c90beec..073313e2bad 100644 --- a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs +++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs @@ -69,7 +69,7 @@ impl IndexRefutableSlice { impl_lint_pass!(IndexRefutableSlice => [INDEX_REFUTABLE_SLICE]); -impl LateLintPass<'_> for IndexRefutableSlice { +impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if_chain! { if !expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some(); diff --git a/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs b/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs index 5fe6725b581..9284e002409 100644 --- a/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs +++ b/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::in_macro; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -46,7 +45,7 @@ impl<'tcx> LateLintPass<'tcx> for NumberedFields { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if let ExprKind::Struct(path, fields, None) = e.kind { if !fields.is_empty() - && !in_macro(e.span) + && !e.span.from_expansion() && fields .iter() .all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit)) 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 0af6b3b7d46..d3bdc819a9f 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 @@ -1,8 +1,7 @@ -use clippy_utils::{diagnostics::span_lint, return_ty, ty::implements_trait}; -use rustc_hir::{ImplItem, ImplItemKind}; +use clippy_utils::{diagnostics::span_lint, get_parent_node, ty::implements_trait}; +use rustc_hir::{def_id::LocalDefId, FnSig, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::symbol::kw; use rustc_span::symbol::sym; declare_clippy_lint! { @@ -40,26 +39,52 @@ declare_clippy_lint! { declare_lint_pass!(IterNotReturningIterator => [ITER_NOT_RETURNING_ITERATOR]); -impl LateLintPass<'_> for IterNotReturningIterator { - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'tcx>) { - let name = impl_item.ident.name.as_str(); - if_chain! { - if let ImplItemKind::Fn(fn_sig, _) = &impl_item.kind; - let ret_ty = return_ty(cx, impl_item.hir_id()); - if matches!(name, "iter" | "iter_mut"); - if let [param] = cx.tcx.fn_arg_names(impl_item.def_id); - if param.name == kw::SelfLower; - if let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator); - if !implements_trait(cx, ret_ty, iter_trait_id, &[]); +impl<'tcx> LateLintPass<'tcx> for IterNotReturningIterator { + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { + let name = item.ident.name.as_str(); + if matches!(name, "iter" | "iter_mut") { + if let TraitItemKind::Fn(fn_sig, _) = &item.kind { + check_sig(cx, name, fn_sig, item.def_id); + } + } + } - then { - span_lint( - cx, - ITER_NOT_RETURNING_ITERATOR, - fn_sig.span, - &format!("this method is named `{}` but its return type does not implement `Iterator`", name), - ); + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'tcx>) { + let name = item.ident.name.as_str(); + if matches!(name, "iter" | "iter_mut") + && !matches!( + get_parent_node(cx.tcx, item.hir_id()), + Some(Node::Item(Item { kind: ItemKind::Impl(i), .. })) if i.of_trait.is_some() + ) + { + if let ImplItemKind::Fn(fn_sig, _) = &item.kind { + check_sig(cx, name, fn_sig, item.def_id); } } } } + +fn check_sig(cx: &LateContext<'_>, name: &str, sig: &FnSig<'_>, fn_id: LocalDefId) { + if sig.decl.implicit_self.has_implicit_self() { + let ret_ty = cx.tcx.fn_sig(fn_id).skip_binder().output(); + let ret_ty = cx + .tcx + .try_normalize_erasing_regions(cx.param_env, ret_ty) + .unwrap_or(ret_ty); + if cx + .tcx + .get_diagnostic_item(sym::Iterator) + .map_or(false, |iter_id| !implements_trait(cx, ret_ty, iter_id, &[])) + { + span_lint( + cx, + ITER_NOT_RETURNING_ITERATOR, + sig.span, + &format!( + "this method is named `{}` but its return type does not implement `Iterator`", + name + ), + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 20e6220ec7d..e1168c3f602 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -214,14 +214,14 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items { let mut current_and_super_traits = DefIdSet::default(); fill_trait_set(visited_trait.def_id.to_def_id(), &mut current_and_super_traits, cx); + let is_empty = sym!(is_empty); let is_empty_method_found = current_and_super_traits .iter() - .flat_map(|&i| cx.tcx.associated_items(i).in_definition_order()) + .flat_map(|&i| cx.tcx.associated_items(i).filter_by_name_unhygienic(is_empty)) .any(|i| { i.kind == ty::AssocKind::Fn && i.fn_has_self_parameter - && i.ident.name == sym!(is_empty) && cx.tcx.fn_sig(i.def_id).inputs().skip_binder().len() == 1 }); @@ -245,7 +245,7 @@ enum LenOutput<'tcx> { Option(DefId), Result(DefId, Ty<'tcx>), } -fn parse_len_output(cx: &LateContext<'_>, sig: FnSig<'tcx>) -> Option<LenOutput<'tcx>> { +fn parse_len_output<'tcx>(cx: &LateContext<'_>, sig: FnSig<'tcx>) -> Option<LenOutput<'tcx>> { match *sig.output().kind() { ty::Int(_) | ty::Uint(_) => Some(LenOutput::Integral), ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Option, adt.did) => { @@ -458,7 +458,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 && item.ident.name.as_str() == "is_empty" { + if item.kind == ty::AssocKind::Fn { let sig = cx.tcx.fn_sig(item.def_id); let ty = sig.skip_binder(); ty.inputs().len() == 1 @@ -469,10 +469,11 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// Checks the inherent impl's items for an `is_empty(self)` method. fn has_is_empty_impl(cx: &LateContext<'_>, id: DefId) -> bool { + let is_empty = sym!(is_empty); cx.tcx.inherent_impls(id).iter().any(|imp| { cx.tcx .associated_items(*imp) - .in_definition_order() + .filter_by_name_unhygienic(is_empty) .any(|item| is_is_empty(cx, item)) }) } @@ -480,9 +481,10 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let ty = &cx.typeck_results().expr_ty(expr).peel_refs(); match ty.kind() { ty::Dynamic(tt, ..) => tt.principal().map_or(false, |principal| { + let is_empty = sym!(is_empty); cx.tcx .associated_items(principal.def_id()) - .in_definition_order() + .filter_by_name_unhygienic(is_empty) .any(|item| is_is_empty(cx, item)) }), ty::Projection(ref proj) => has_is_empty_impl(cx, proj.item_def_id), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_all.rs b/src/tools/clippy/clippy_lints/src/lib.register_all.rs index 944411087e9..26fb4259952 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_all.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_all.rs @@ -37,6 +37,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(derivable_impls::DERIVABLE_IMPLS), LintId::of(derive::DERIVE_HASH_XOR_EQ), LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), + LintId::of(disallowed_methods::DISALLOWED_METHODS), + LintId::of(disallowed_types::DISALLOWED_TYPES), LintId::of(doc::MISSING_SAFETY_DOC), LintId::of(doc::NEEDLESS_DOCTEST_MAIN), LintId::of(double_comparison::DOUBLE_COMPARISONS), @@ -113,6 +115,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(loops::WHILE_LET_ON_ITERATOR), LintId::of(main_recursion::MAIN_RECURSION), LintId::of(manual_async_fn::MANUAL_ASYNC_FN), + LintId::of(manual_bits::MANUAL_BITS), LintId::of(manual_map::MANUAL_MAP), LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(manual_strip::MANUAL_STRIP), @@ -204,7 +207,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(mut_key::MUTABLE_KEY_TYPE), LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK), LintId::of(mut_reference::UNNECESSARY_MUT_PASSED), - LintId::of(mutex_atomic::MUTEX_ATOMIC), LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), LintId::of(needless_bool::BOOL_COMPARISON), LintId::of(needless_bool::NEEDLESS_BOOL), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_lints.rs b/src/tools/clippy/clippy_lints/src/lib.register_lints.rs index 002122793f3..746bdb19c3d 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_lints.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_lints.rs @@ -3,33 +3,33 @@ // Manual edits will be overwritten. store.register_lints(&[ - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::CLIPPY_LINTS_INTERNAL, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::COMPILER_LINT_FUNCTIONS, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::DEFAULT_LINT, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::IF_CHAIN_STYLE, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::INTERNING_DEFINED_SYMBOL, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::INVALID_PATHS, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::LINT_WITHOUT_LINT_PASS, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::OUTER_EXPN_EXPN_DATA, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::PRODUCE_ICE, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::UNNECESSARY_SYMBOL_STR, absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS, approx_const::APPROX_CONSTANT, @@ -59,6 +59,7 @@ store.register_lints(&[ bool_assert_comparison::BOOL_ASSERT_COMPARISON, booleans::LOGIC_BUG, booleans::NONMINIMAL_BOOL, + borrow_as_ptr::BORROW_AS_PTR, bytecount::NAIVE_BYTECOUNT, cargo_common_metadata::CARGO_COMMON_METADATA, case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, @@ -225,6 +226,7 @@ store.register_lints(&[ main_recursion::MAIN_RECURSION, manual_assert::MANUAL_ASSERT, manual_async_fn::MANUAL_ASYNC_FN, + manual_bits::MANUAL_BITS, manual_map::MANUAL_MAP, manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, manual_ok_or::MANUAL_OK_OR, @@ -435,6 +437,7 @@ store.register_lints(&[ shadow::SHADOW_REUSE, shadow::SHADOW_SAME, shadow::SHADOW_UNRELATED, + single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES, single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, diff --git a/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs b/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs index e3cf0670018..a7353790100 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs @@ -6,8 +6,6 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR), LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY), LintId::of(copies::BRANCHES_SHARING_CODE), - LintId::of(disallowed_methods::DISALLOWED_METHODS), - LintId::of(disallowed_types::DISALLOWED_TYPES), LintId::of(equatable_if_let::EQUATABLE_IF_LET), LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM), LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS), @@ -17,6 +15,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(let_if_seq::USELESS_LET_IF_SEQ), LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN), LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL), + LintId::of(mutex_atomic::MUTEX_ATOMIC), LintId::of(mutex_atomic::MUTEX_INTEGER), LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY), LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs b/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs index 70a4a624378..1744b7c8250 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs @@ -7,6 +7,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK), LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(bit_mask::VERBOSE_BIT_MASK), + LintId::of(borrow_as_ptr::BORROW_AS_PTR), LintId::of(bytecount::NAIVE_BYTECOUNT), LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS), LintId::of(casts::CAST_LOSSLESS), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_perf.rs b/src/tools/clippy/clippy_lints/src/lib.register_perf.rs index 2ea0b696f1f..f9ffd4cf829 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_perf.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_perf.rs @@ -19,7 +19,6 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ LintId::of(methods::SINGLE_CHAR_PATTERN), LintId::of(methods::UNNECESSARY_TO_OWNED), LintId::of(misc::CMP_OWNED), - LintId::of(mutex_atomic::MUTEX_ATOMIC), LintId::of(redundant_clone::REDUNDANT_CLONE), LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs b/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs index eab389a9bd8..e7e2798da7d 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs @@ -54,6 +54,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(shadow::SHADOW_REUSE), LintId::of(shadow::SHADOW_SAME), LintId::of(shadow::SHADOW_UNRELATED), + LintId::of(single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES), LintId::of(strings::STRING_ADD), LintId::of(strings::STRING_SLICE), LintId::of(strings::STRING_TO_STRING), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_style.rs b/src/tools/clippy/clippy_lints/src/lib.register_style.rs index 1a0b869d40a..05211476ff2 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_style.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_style.rs @@ -16,6 +16,8 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(comparison_chain::COMPARISON_CHAIN), LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), LintId::of(dereference::NEEDLESS_BORROW), + LintId::of(disallowed_methods::DISALLOWED_METHODS), + LintId::of(disallowed_types::DISALLOWED_TYPES), LintId::of(doc::MISSING_SAFETY_DOC), LintId::of(doc::NEEDLESS_DOCTEST_MAIN), LintId::of(enum_variants::ENUM_VARIANT_NAMES), @@ -41,6 +43,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(loops::WHILE_LET_ON_ITERATOR), LintId::of(main_recursion::MAIN_RECURSION), LintId::of(manual_async_fn::MANUAL_ASYNC_FN), + LintId::of(manual_bits::MANUAL_BITS), LintId::of(manual_map::MANUAL_MAP), LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(map_clone::MAP_CLONE), diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index d4687a1e287..79e9882fef4 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -4,7 +4,6 @@ #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(drain_filter)] -#![feature(in_band_lifetimes)] #![feature(iter_intersperse)] #![feature(let_else)] #![feature(once_cell)] @@ -153,12 +152,9 @@ macro_rules! declare_clippy_lint { }; } -#[cfg(feature = "metadata-collector-lint")] +#[cfg(feature = "internal")] mod deprecated_lints; -#[cfg_attr( - any(feature = "internal-lints", feature = "metadata-collector-lint"), - allow(clippy::missing_clippy_version_attribute) -)] +#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))] mod utils; // begin lints modules, do not remove this comment, it’s used in `update_lints` @@ -177,6 +173,7 @@ mod blacklisted_name; mod blocks_in_if_conditions; mod bool_assert_comparison; mod booleans; +mod borrow_as_ptr; mod bytecount; mod cargo_common_metadata; mod case_sensitive_file_extension_comparisons; @@ -264,6 +261,7 @@ mod macro_use; mod main_recursion; mod manual_assert; mod manual_async_fn; +mod manual_bits; mod manual_map; mod manual_non_exhaustive; mod manual_ok_or; @@ -351,6 +349,7 @@ mod self_named_constructors; mod semicolon_if_nothing_returned; mod serde_api; mod shadow; +mod single_char_lifetime_names; mod single_component_path_imports; mod size_of_in_element_count; mod slow_vector_initialization; @@ -471,7 +470,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: include!("lib.register_restriction.rs"); include!("lib.register_pedantic.rs"); - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] include!("lib.register_internal.rs"); include!("lib.register_all.rs"); @@ -483,7 +482,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: include!("lib.register_cargo.rs"); include!("lib.register_nursery.rs"); - #[cfg(feature = "metadata-collector-lint")] + #[cfg(feature = "internal")] { if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) { store.register_late_pass(|| Box::new(utils::internal_lints::metadata_collector::MetadataCollector::new())); @@ -492,7 +491,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: } // all the internal lints - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] { store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal)); store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce)); @@ -858,6 +857,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(needless_late_init::NeedlessLateInit)); store.register_late_pass(|| Box::new(return_self_not_must_use::ReturnSelfNotMustUse)); store.register_late_pass(|| Box::new(init_numbered_fields::NumberedFields)); + store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames)); + store.register_late_pass(move || Box::new(borrow_as_ptr::BorrowAsPtr::new(msrv))); + store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/tools/clippy/clippy_lints/src/loops/empty_loop.rs b/src/tools/clippy/clippy_lints/src/loops/empty_loop.rs index dda09fecdf9..823cf0f4322 100644 --- a/src/tools/clippy/clippy_lints/src/loops/empty_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/empty_loop.rs @@ -5,7 +5,7 @@ use clippy_utils::{is_in_panic_handler, is_no_std_crate}; use rustc_hir::{Block, Expr}; use rustc_lint::LateContext; -pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, loop_block: &Block<'_>) { if loop_block.stmts.is_empty() && loop_block.expr.is_none() && !is_in_panic_handler(cx, expr) { let msg = "empty `loop {}` wastes CPU cycles"; let help = if is_no_std_crate(cx) { diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs index 1bab0d99b69..17246cc5426 100644 --- a/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs @@ -8,7 +8,7 @@ use rustc_lint::LateContext; use rustc_middle::ty::TyS; use rustc_span::symbol::sym; -pub(super) fn check(cx: &LateContext<'_>, self_arg: &'hir Expr<'hir>, call_expr: &Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, call_expr: &Expr<'_>) { let self_ty = cx.typeck_results().expr_ty(self_arg); let self_ty_adjusted = cx.typeck_results().expr_ty_adjusted(self_arg); if !(TyS::same_type(self_ty, self_ty_adjusted) && is_trait_method(cx, call_expr, sym::IntoIterator)) { diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs index c62fa5e998b..48c4015e07b 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs @@ -2,7 +2,7 @@ use super::{IncrementVisitor, InitializeVisitor, MANUAL_MEMCPY}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_copy; use clippy_utils::{get_enclosing_block, higher, path_to_local, sugg}; use if_chain::if_chain; use rustc_ast::ast; @@ -62,15 +62,15 @@ pub(super) fn check<'tcx>( if_chain! { if let ExprKind::Index(base_left, idx_left) = lhs.kind; if let ExprKind::Index(base_right, idx_right) = rhs.kind; - if is_slice_like(cx, cx.typeck_results().expr_ty(base_left)); - if is_slice_like(cx, cx.typeck_results().expr_ty(base_right)); + if let Some(ty) = get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_left)); + if get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_right)).is_some(); if let Some((start_left, offset_left)) = get_details_from_idx(cx, idx_left, &starts); if let Some((start_right, offset_right)) = get_details_from_idx(cx, idx_right, &starts); // Source and destination must be different if path_to_local(base_left) != path_to_local(base_right); then { - Some((IndexExpr { base: base_left, idx: start_left, idx_offset: offset_left }, + Some((ty, IndexExpr { base: base_left, idx: start_left, idx_offset: offset_left }, IndexExpr { base: base_right, idx: start_right, idx_offset: offset_right })) } else { None @@ -78,7 +78,7 @@ pub(super) fn check<'tcx>( } }) }) - .map(|o| o.map(|(dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, &dst, &src))) + .map(|o| o.map(|(ty, dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, ty, &dst, &src))) .collect::<Option<Vec<_>>>() .filter(|v| !v.is_empty()) .map(|v| v.join("\n ")); @@ -105,6 +105,7 @@ fn build_manual_memcpy_suggestion<'tcx>( start: &Expr<'_>, end: &Expr<'_>, limits: ast::RangeLimits, + elem_ty: Ty<'tcx>, dst: &IndexExpr<'_>, src: &IndexExpr<'_>, ) -> String { @@ -187,9 +188,16 @@ fn build_manual_memcpy_suggestion<'tcx>( .into() }; + let method_str = if is_copy(cx, elem_ty) { + "copy_from_slice" + } else { + "clone_from_slice" + }; + format!( - "{}.clone_from_slice(&{}[{}..{}]);", + "{}.{}(&{}[{}..{}]);", dst, + method_str, src_base_str, src_offset.maybe_par(), src_limit.maybe_par() @@ -203,7 +211,7 @@ fn build_manual_memcpy_suggestion<'tcx>( #[derive(Clone)] struct MinifyingSugg<'a>(Sugg<'a>); -impl Display for MinifyingSugg<'a> { +impl<'a> Display for MinifyingSugg<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) } @@ -324,14 +332,13 @@ struct Start<'hir> { kind: StartKind<'hir>, } -fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'_>) -> bool { - let is_slice = match ty.kind() { - ty::Ref(_, subty, _) => is_slice_like(cx, subty), - ty::Slice(..) | ty::Array(..) => true, - _ => false, - }; - - is_slice || is_type_diagnostic_item(cx, ty, sym::Vec) || is_type_diagnostic_item(cx, ty, sym::VecDeque) +fn get_slice_like_element_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { + match ty.kind() { + ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Vec, adt.did) => Some(subs.type_at(0)), + ty::Ref(_, subty, _) => get_slice_like_element_ty(cx, subty), + ty::Slice(ty) | ty::Array(ty, _) => Some(ty), + _ => None, + } } fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { 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 aedf0844937..37a57d8feb1 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 @@ -147,7 +147,7 @@ impl BreakAfterExprVisitor { } } -impl intravisit::Visitor<'tcx> for BreakAfterExprVisitor { +impl<'tcx> intravisit::Visitor<'tcx> for BreakAfterExprVisitor { type Map = Map<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs b/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs index ba895f35faa..6248680aa62 100644 --- a/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs @@ -339,8 +339,8 @@ fn detect_iter_and_into_iters<'tcx: 'a, 'a>( } } -fn get_captured_ids(cx: &LateContext<'tcx>, ty: &'_ TyS<'_>) -> HirIdSet { - fn get_captured_ids_recursive(cx: &LateContext<'tcx>, ty: &'_ TyS<'_>, set: &mut HirIdSet) { +fn get_captured_ids(cx: &LateContext<'_>, ty: &'_ TyS<'_>) -> HirIdSet { + fn get_captured_ids_recursive(cx: &LateContext<'_>, ty: &'_ TyS<'_>, set: &mut HirIdSet) { match ty.kind() { ty::Adt(_, generics) => { for generic in *generics { 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 a3aa6be6afd..bb1b3e2a1ec 100644 --- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs @@ -10,8 +10,8 @@ use rustc_span::Span; use std::iter::{once, Iterator}; pub(super) fn check( - cx: &LateContext<'tcx>, - block: &'tcx Block<'_>, + cx: &LateContext<'_>, + block: &Block<'_>, loop_id: HirId, span: Span, for_loop: Option<&ForLoop<'_>>, diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs index 4dcd5c87722..8f57df0be6b 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs @@ -7,7 +7,7 @@ use rustc_hir::{Block, Expr, ExprKind, MatchSource, Pat, StmtKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; -pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) { // extract the expression from the first statement (if any) in a block let inner_stmt_expr = extract_expr_from_first_stmt(loop_block); // or extract the first expression (if any) from the block diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs index b390476a664..750328d1d01 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs @@ -8,12 +8,13 @@ use clippy_utils::{ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}; -use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, PatKind, QPath, UnOp}; +use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp}; use rustc_lint::LateContext; -use rustc_span::{symbol::sym, Span, Symbol}; +use rustc_middle::ty::adjustment::Adjust; +use rustc_span::{symbol::sym, Symbol}; -pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let (scrutinee_expr, iter_expr, some_pat, loop_expr) = if_chain! { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + let (scrutinee_expr, iter_expr_struct, iter_expr, some_pat, loop_expr) = if_chain! { if let Some(higher::WhileLet { if_then, let_pat, let_expr }) = higher::WhileLet::hir(expr); // check for `Some(..)` pattern if let PatKind::TupleStruct(QPath::Resolved(None, pat_path), some_pat, _) = let_pat.kind; @@ -27,7 +28,7 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // get the loop containing the match expression if !uses_iter(cx, &iter_expr_struct, if_then); then { - (let_expr, iter_expr_struct, some_pat, expr) + (let_expr, iter_expr_struct, iter_expr, some_pat, expr) } else { return; } @@ -47,7 +48,11 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // If the iterator is a field or the iterator is accessed after the loop is complete it needs to be // borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used // afterwards a mutable borrow of a field isn't necessary. - let by_ref = if !iter_expr.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr, loop_expr) { + let by_ref = if cx.typeck_results().expr_ty(iter_expr).ref_mutability() == Some(Mutability::Mut) + || !iter_expr_struct.can_move + || !iter_expr_struct.fields.is_empty() + || needs_mutable_borrow(cx, &iter_expr_struct, loop_expr) + { ".by_ref()" } else { "" @@ -67,26 +72,36 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { #[derive(Debug)] struct IterExpr { - /// The span of the whole expression, not just the path and fields stored here. - span: Span, /// The fields used, in order of child to parent. fields: Vec<Symbol>, /// The path being used. path: Res, + /// Whether or not the iterator can be moved. + can_move: bool, } /// Parses any expression to find out which field of which variable is used. Will return `None` if /// the expression might have side effects. fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExpr> { - let span = e.span; let mut fields = Vec::new(); + let mut can_move = true; loop { + if cx + .typeck_results() + .expr_adjustments(e) + .iter() + .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))) + { + // Custom deref impls need to borrow the whole value as it's captured by reference + can_move = false; + fields.clear(); + } match e.kind { ExprKind::Path(ref path) => { break Some(IterExpr { - span, fields, path: cx.qpath_res(path, e.hir_id), + can_move, }); }, ExprKind::Field(base, name) => { @@ -99,10 +114,12 @@ fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExp // Shouldn't have side effects, but there's no way to trace which field is used. So forget which fields have // already been seen. ExprKind::Index(base, idx) if !idx.can_have_side_effects() => { + can_move = false; fields.clear(); e = base; }, ExprKind::Unary(UnOp::Deref, base) => { + can_move = false; fields.clear(); e = base; }, @@ -174,7 +191,7 @@ fn is_expr_same_child_or_parent_field(cx: &LateContext<'_>, expr: &Expr<'_>, fie /// Strips off all field and path expressions. This will return true if a field or path has been /// skipped. Used to skip them after failing to check for equality. -fn skip_fields_and_path(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool) { +fn skip_fields_and_path<'tcx>(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool) { let mut e = expr; let e = loop { match e.kind { @@ -187,13 +204,13 @@ fn skip_fields_and_path(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool } /// Checks if the given expression uses the iterator. -fn uses_iter(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr<'_>) -> bool { +fn uses_iter<'tcx>(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr<'_>) -> bool { struct V<'a, 'b, 'tcx> { cx: &'a LateContext<'tcx>, iter_expr: &'b IterExpr, uses_iter: bool, } - impl Visitor<'tcx> for V<'_, '_, 'tcx> { + impl<'tcx> Visitor<'tcx> for V<'_, '_, 'tcx> { type Map = ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { NestedVisitorMap::None @@ -228,7 +245,7 @@ fn uses_iter(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr } #[allow(clippy::too_many_lines)] -fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr: &'tcx Expr<'_>) -> bool { +fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: &Expr<'_>) -> bool { struct AfterLoopVisitor<'a, 'b, 'tcx> { cx: &'a LateContext<'tcx>, iter_expr: &'b IterExpr, @@ -236,7 +253,7 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr: after_loop: bool, used_iter: bool, } - impl Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> { + impl<'tcx> Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> { type Map = ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { NestedVisitorMap::None @@ -275,7 +292,7 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr: found_local: bool, used_after: bool, } - impl Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> { + impl<'a, 'b, 'tcx> Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> { type Map = ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs index 50d80e6a1d2..ca1ccb93bf3 100644 --- a/src/tools/clippy/clippy_lints/src/macro_use.rs +++ b/src/tools/clippy/clippy_lints/src/macro_use.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::in_macro; use clippy_utils::source::snippet; use hir::def::{DefKind, Res}; use if_chain::if_chain; @@ -96,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { if let Res::Def(DefKind::Mod, id) = path.res; if !id.is_local(); then { - for kid in cx.tcx.item_children(id).iter() { + for kid in cx.tcx.module_children(id).iter() { if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res { let span = mac_attr.span; let def_path = cx.tcx.def_path_str(mac_id); @@ -104,34 +103,34 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { } } } else { - if in_macro(item.span) { + if item.span.from_expansion() { self.push_unique_macro_pat_ty(cx, item.span); } } } } fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) { - if in_macro(attr.span) { + if attr.span.from_expansion() { self.push_unique_macro(cx, attr.span); } } fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { - if in_macro(expr.span) { + if expr.span.from_expansion() { self.push_unique_macro(cx, expr.span); } } fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &hir::Stmt<'_>) { - if in_macro(stmt.span) { + if stmt.span.from_expansion() { self.push_unique_macro(cx, stmt.span); } } fn check_pat(&mut self, cx: &LateContext<'_>, pat: &hir::Pat<'_>) { - if in_macro(pat.span) { + if pat.span.from_expansion() { self.push_unique_macro_pat_ty(cx, pat.span); } } fn check_ty(&mut self, cx: &LateContext<'_>, ty: &hir::Ty<'_>) { - if in_macro(ty.span) { + if ty.span.from_expansion() { self.push_unique_macro_pat_ty(cx, ty.span); } } diff --git a/src/tools/clippy/clippy_lints/src/manual_assert.rs b/src/tools/clippy/clippy_lints/src/manual_assert.rs index 5a2a965716c..26b53ab5d68 100644 --- a/src/tools/clippy/clippy_lints/src/manual_assert.rs +++ b/src/tools/clippy/clippy_lints/src/manual_assert.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::higher::PanicExpn; +use clippy_utils::macros::{root_macro_call, FormatArgsExpn}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_expn_of, sugg}; +use clippy_utils::{peel_blocks_with_stmt, sugg}; use rustc_errors::Applicability; -use rustc_hir::{Block, Expr, ExprKind, StmtKind, UnOp}; +use rustc_hir::{Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -34,65 +35,34 @@ declare_clippy_lint! { declare_lint_pass!(ManualAssert => [MANUAL_ASSERT]); -impl LateLintPass<'_> for ManualAssert { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { +impl<'tcx> LateLintPass<'tcx> for ManualAssert { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if_chain! { - if let Expr { - kind: ExprKind:: If(cond, Expr { - kind: ExprKind::Block( - Block { - stmts: [stmt], - .. - }, - _), - .. - }, None), - .. - } = &expr; - if is_expn_of(stmt.span, "panic").is_some(); + if let ExprKind::If(cond, then, None) = expr.kind; if !matches!(cond.kind, ExprKind::Let(_)); - if let StmtKind::Semi(semi) = stmt.kind; + if !expr.span.from_expansion(); + let then = peel_blocks_with_stmt(then); + if let Some(macro_call) = root_macro_call(then.span); + if cx.tcx.item_name(macro_call.def_id) == sym::panic; if !cx.tcx.sess.source_map().is_multiline(cond.span); - + if let Some(format_args) = FormatArgsExpn::find_nested(cx, then, macro_call.expn); then { - let call = if_chain! { - if let ExprKind::Block(block, _) = semi.kind; - if let Some(init) = block.expr; - then { - init - } else { - semi - } - }; - let span = if let Some(panic_expn) = PanicExpn::parse(call) { - match *panic_expn.format_args.value_args { - [] => panic_expn.format_args.format_string_span, - [.., last] => panic_expn.format_args.format_string_span.to(last.span), - } - } else if let ExprKind::Call(_, [format_args]) = call.kind { - format_args.span - } else { - return - }; let mut applicability = Applicability::MachineApplicable; - let sugg = snippet_with_applicability(cx, span, "..", &mut applicability); - let cond_sugg = if let ExprKind::DropTemps(e, ..) = cond.kind { - if let Expr{kind: ExprKind::Unary(UnOp::Not, not_expr), ..} = e { - sugg::Sugg::hir_with_applicability(cx, not_expr, "..", &mut applicability).maybe_par().to_string() - } else { - format!("!{}", sugg::Sugg::hir_with_applicability(cx, e, "..", &mut applicability).maybe_par()) - } - } else { - format!("!{}", sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par()) + let format_args_snip = snippet_with_applicability(cx, format_args.inputs_span(), "..", &mut applicability); + let cond = cond.peel_drop_temps(); + let (cond, not) = match cond.kind { + ExprKind::Unary(UnOp::Not, e) => (e, ""), + _ => (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});"); span_lint_and_sugg( cx, MANUAL_ASSERT, expr.span, "only a `panic!` in `if`-then statement", "try", - format!("assert!({}, {});", cond_sugg, sugg), + sugg, Applicability::MachineApplicable, ); } diff --git a/src/tools/clippy/clippy_lints/src/manual_bits.rs b/src/tools/clippy/clippy_lints/src/manual_bits.rs new file mode 100644 index 00000000000..50bf2527e39 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/manual_bits.rs @@ -0,0 +1,107 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_opt; +use clippy_utils::{match_def_path, meets_msrv, msrvs, paths}; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Ty}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for uses of `std::mem::size_of::<T>() * 8` when + /// `T::BITS` is available. + /// + /// ### Why is this bad? + /// Can be written as the shorter `T::BITS`. + /// + /// ### Example + /// ```rust + /// std::mem::size_of::<usize>() * 8; + /// ``` + /// Use instead: + /// ```rust + /// usize::BITS; + /// ``` + #[clippy::version = "1.60.0"] + pub MANUAL_BITS, + style, + "manual implementation of `size_of::<T>() * 8` can be simplified with `T::BITS`" +} + +#[derive(Clone)] +pub struct ManualBits { + msrv: Option<RustcVersion>, +} + +impl ManualBits { + #[must_use] + pub fn new(msrv: Option<RustcVersion>) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(ManualBits => [MANUAL_BITS]); + +impl<'tcx> LateLintPass<'tcx> for ManualBits { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if !meets_msrv(self.msrv.as_ref(), &msrvs::MANUAL_BITS) { + return; + } + + if_chain! { + if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind; + if let BinOpKind::Mul = &bin_op.node; + if let Some((real_ty, resolved_ty, other_expr)) = get_one_size_of_ty(cx, left_expr, right_expr); + if matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_)); + if let ExprKind::Lit(lit) = &other_expr.kind; + if let LitKind::Int(8, _) = lit.node; + + then { + span_lint_and_sugg( + cx, + MANUAL_BITS, + expr.span, + "usage of `mem::size_of::<T>()` to obtain the size of `T` in bits", + "consider using", + format!("{}::BITS", snippet_opt(cx, real_ty.span).unwrap()), + Applicability::MachineApplicable, + ); + } + } + } +} + +fn get_one_size_of_ty<'tcx>( + cx: &LateContext<'tcx>, + expr1: &'tcx Expr<'_>, + expr2: &'tcx Expr<'_>, +) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>, &'tcx Expr<'tcx>)> { + match (get_size_of_ty(cx, expr1), get_size_of_ty(cx, expr2)) { + (Some((real_ty, resolved_ty)), None) => Some((real_ty, resolved_ty, expr2)), + (None, Some((real_ty, resolved_ty))) => Some((real_ty, resolved_ty, expr1)), + _ => None, + } +} + +fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>)> { + if_chain! { + if let ExprKind::Call(count_func, _func_args) = expr.kind; + if let ExprKind::Path(ref count_func_qpath) = count_func.kind; + + if let QPath::Resolved(_, count_func_path) = count_func_qpath; + if let Some(segment_zero) = count_func_path.segments.get(0); + if let Some(args) = segment_zero.args; + if let Some(GenericArg::Type(real_ty)) = args.args.get(0); + + if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::MEM_SIZE_OF); + then { + cx.typeck_results().node_substs(count_func.hir_id).types().next().map(|resolved_ty| (real_ty, resolved_ty)) + } else { + None + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/manual_map.rs b/src/tools/clippy/clippy_lints/src/manual_map.rs index 34a70ca76c6..8475e367b09 100644 --- a/src/tools/clippy/clippy_lints/src/manual_map.rs +++ b/src/tools/clippy/clippy_lints/src/manual_map.rs @@ -45,7 +45,7 @@ declare_clippy_lint! { declare_lint_pass!(ManualMap => [MANUAL_MAP]); -impl LateLintPass<'_> for ManualMap { +impl<'tcx> LateLintPass<'tcx> for ManualMap { #[allow(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let (scrutinee, then_pat, then_body, else_pat, else_body) = match IfLetOrMatch::parse(cx, expr) { @@ -219,7 +219,7 @@ impl LateLintPass<'_> for ManualMap { // Checks whether the expression could be passed as a function, or whether a closure is needed. // Returns the function to be passed to `map` if it exists. -fn can_pass_as_func(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { +fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { match expr.kind { ExprKind::Call(func, [arg]) if path_to_local_id(arg, binding) @@ -251,8 +251,13 @@ struct SomeExpr<'tcx> { // Try to parse into a recognized `Option` pattern. // i.e. `_`, `None`, `Some(..)`, or a reference to any of those. -fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> { - fn f(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ref_count: usize, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> { +fn try_parse_pattern<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> { + fn f<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + ref_count: usize, + ctxt: SyntaxContext, + ) -> Option<OptionPat<'tcx>> { match pat.kind { PatKind::Wild => Some(OptionPat::Wild), PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), @@ -269,7 +274,7 @@ fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxCon } // Checks for an expression wrapped by the `Some` constructor. Returns the contained expression. -fn get_some_expr( +fn get_some_expr<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, needs_unsafe_block: bool, @@ -306,6 +311,6 @@ fn get_some_expr( } // Checks for the `None` value. -fn is_none_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { +fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { matches!(peel_blocks(expr).kind, ExprKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone)) } diff --git a/src/tools/clippy/clippy_lints/src/manual_ok_or.rs b/src/tools/clippy/clippy_lints/src/manual_ok_or.rs index b60e2dc366b..bd083e3e9e2 100644 --- a/src/tools/clippy/clippy_lints/src/manual_ok_or.rs +++ b/src/tools/clippy/clippy_lints/src/manual_ok_or.rs @@ -40,7 +40,7 @@ declare_clippy_lint! { declare_lint_pass!(ManualOkOr => [MANUAL_OK_OR]); -impl LateLintPass<'_> for ManualOkOr { +impl<'tcx> LateLintPass<'tcx> for ManualOkOr { fn check_expr(&mut self, cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'tcx>) { if in_external_macro(cx.sess(), scrutinee.span) { return; diff --git a/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs index aac3c6e0de2..b3a91d9f18f 100644 --- a/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs @@ -43,7 +43,7 @@ declare_clippy_lint! { declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]); -impl LateLintPass<'_> for ManualUnwrapOr { +impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOr { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) { return; diff --git a/src/tools/clippy/clippy_lints/src/match_str_case_mismatch.rs b/src/tools/clippy/clippy_lints/src/match_str_case_mismatch.rs index 2c0fc218ca0..1fc7eb72142 100644 --- a/src/tools/clippy/clippy_lints/src/match_str_case_mismatch.rs +++ b/src/tools/clippy/clippy_lints/src/match_str_case_mismatch.rs @@ -55,7 +55,7 @@ enum CaseMethod { AsciiUppercase, } -impl LateLintPass<'_> for MatchStrCaseMismatch { +impl<'tcx> LateLintPass<'tcx> for MatchStrCaseMismatch { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { if !in_external_macro(cx.tcx.sess, expr.span); diff --git a/src/tools/clippy/clippy_lints/src/matches.rs b/src/tools/clippy/clippy_lints/src/matches.rs index 22970507f96..60dd957db01 100644 --- a/src/tools/clippy/clippy_lints/src/matches.rs +++ b/src/tools/clippy/clippy_lints/src/matches.rs @@ -2,16 +2,17 @@ use clippy_utils::consts::{constant, constant_full_int, miri_to_const, FullInt}; use clippy_utils::diagnostics::{ multispan_sugg, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, }; -use clippy_utils::higher; +use clippy_utils::macros::{is_panic, root_macro_call}; use clippy_utils::source::{expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs}; use clippy_utils::visitors::is_local_used; use clippy_utils::{ - get_parent_expr, is_expn_of, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild, meets_msrv, msrvs, + get_parent_expr, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild, meets_msrv, msrvs, path_to_local, path_to_local_id, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, strip_pat_refs, }; +use clippy_utils::{higher, peel_blocks_with_stmt}; use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash}; use core::iter::{once, ExactSizeIterator}; use if_chain::if_chain; @@ -974,7 +975,8 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm } if_chain! { if matching_wild; - if is_panic_call(arm.body); + if let Some(macro_call) = root_macro_call(peel_blocks_with_stmt(arm.body).span); + if is_panic(cx, macro_call.def_id); then { // `Err(_)` or `Err(_e)` arm with `panic!` found span_lint_and_note(cx, @@ -997,7 +999,7 @@ enum CommonPrefixSearcher<'a> { Path(&'a [PathSegment<'a>]), Mixed, } -impl CommonPrefixSearcher<'a> { +impl<'a> CommonPrefixSearcher<'a> { fn with_path(&mut self, path: &'a [PathSegment<'a>]) { match path { [path @ .., _] => self.with_prefix(path), @@ -1136,7 +1138,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) s.push_str("::"); s }, - variant.ident.name, + variant.name, match variant.ctor_kind { CtorKind::Fn if variant.fields.len() == 1 => "(_)", CtorKind::Fn => "(..)", @@ -1179,22 +1181,6 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) }; } -// If the block contains only a `panic!` macro (as expression or statement) -fn is_panic_call(expr: &Expr<'_>) -> bool { - // Unwrap any wrapping blocks - let span = if let ExprKind::Block(block, _) = expr.kind { - match (&block.expr, block.stmts.len(), block.stmts.first()) { - (&Some(exp), 0, _) => exp.span, - (&None, 1, Some(stmt)) => stmt.span, - _ => return false, - } - } else { - expr.span - }; - - is_expn_of(span, "panic").is_some() && is_expn_of(span, "unreachable").is_none() -} - fn check_match_ref_pats<'a, 'b, I>(cx: &LateContext<'_>, ex: &Expr<'_>, pats: I, expr: &Expr<'_>) where 'b: 'a, @@ -1818,11 +1804,15 @@ mod redundant_pattern_match { /// Checks if the drop order for a type matters. Some std types implement drop solely to /// deallocate memory. For these types, and composites containing them, changing the drop order /// won't result in any observable side effects. - fn type_needs_ordered_drop(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + fn type_needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { type_needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default()) } - fn type_needs_ordered_drop_inner(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool { + fn type_needs_ordered_drop_inner<'tcx>( + cx: &LateContext<'tcx>, + ty: Ty<'tcx>, + seen: &mut FxHashSet<Ty<'tcx>>, + ) -> bool { if !seen.insert(ty) { return false; } @@ -1884,7 +1874,7 @@ mod redundant_pattern_match { // Checks if there are any temporaries created in the given expression for which drop order // matters. - fn temporaries_need_ordered_drop(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + fn temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { struct V<'a, 'tcx> { cx: &'a LateContext<'tcx>, res: bool, 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 0ec9387f9c4..0f39470f342 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 @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::higher::FormatExpn; +use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; @@ -14,7 +14,13 @@ use super::EXPECT_FUN_CALL; /// Checks for the `EXPECT_FUN_CALL` lint. #[allow(clippy::too_many_lines)] -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_span: Span, name: &str, args: &[hir::Expr<'_>]) { +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &hir::Expr<'_>, + method_span: Span, + name: &str, + args: &'tcx [hir::Expr<'tcx>], +) { // Strip `&`, `as_ref()` and `as_str()` off `arg` until we're left with either a `String` or // `&str` fn get_arg_root<'a>(cx: &LateContext<'_>, arg: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { @@ -128,11 +134,12 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_span: Spa let mut applicability = Applicability::MachineApplicable; //Special handling for `format!` as arg_root - if let Some(format_expn) = FormatExpn::parse(arg_root) { - let span = match *format_expn.format_args.value_args { - [] => format_expn.format_args.format_string_span, - [.., last] => format_expn.format_args.format_string_span.to(last.span), - }; + if let Some(macro_call) = root_macro_call_first_node(cx, arg_root) { + if !cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) { + return; + } + let Some(format_args) = FormatArgsExpn::find_nested(cx, arg_root, macro_call.expn) else { return }; + let span = format_args.inputs_span(); let sugg = snippet_with_applicability(cx, span, "..", &mut applicability); span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs index 8ea9312c0f7..6436e28a63c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -37,7 +37,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Exp } } -fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'tcx>) -> String { +fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'_>) -> String { fn strip_angle_brackets(s: &str) -> Option<&str> { s.strip_prefix('<')?.strip_suffix('>') } diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 4e33b2ff14c..1041f644e32 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -80,7 +80,6 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty, TyS}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::symbol::Symbol; use rustc_span::{sym, Span}; use rustc_typeck::hir_ty_to_ty; @@ -1895,6 +1894,11 @@ declare_clippy_lint! { /// ### Why is this bad? /// The unnecessary calls result in useless allocations. /// + /// ### Known problems + /// `unnecessary_to_owned` can falsely trigger if `IntoIterator::into_iter` is applied to an + /// owned copy of a resource and the resource is later used mutably. See + /// [#8148](https://github.com/rust-lang/rust-clippy/issues/8148). + /// /// ### Example /// ```rust /// let path = std::path::Path::new("x"); @@ -1997,24 +2001,16 @@ impl_lint_pass!(Methods => [ ]); /// Extracts a method call name, args, and `Span` of the method name. -fn method_call<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> Option<(Symbol, &'tcx [hir::Expr<'tcx>], Span)> { +fn method_call<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx str, &'tcx [hir::Expr<'tcx>], Span)> { if let ExprKind::MethodCall(path, span, args, _) = recv.kind { if !args.iter().any(|e| e.span.from_expansion()) { - return Some((path.ident.name, args, span)); + let name = path.ident.name.as_str(); + return Some((name, args, span)); } } None } -/// Same as `method_call` but the `Symbol` is dereferenced into a temporary `&str` -macro_rules! method_call { - ($expr:expr) => { - method_call($expr) - .as_ref() - .map(|&(ref name, args, span)| (name.as_str(), args, span)) - }; -} - impl<'tcx> LateLintPass<'tcx> for Methods { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if expr.span.from_expansion() { @@ -2217,7 +2213,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { #[allow(clippy::too_many_lines)] fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Option<&RustcVersion>) { - if let Some((name, [recv, args @ ..], span)) = method_call!(expr) { + if let Some((name, [recv, args @ ..], span)) = method_call(expr) { match (name, args) { ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => { zst_offset::check(cx, expr, recv); @@ -2233,7 +2229,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, msrv), - ("collect", []) => match method_call!(recv) { + ("collect", []) => match method_call(recv) { Some((name @ ("cloned" | "copied"), [recv2], _)) => { iter_cloned_collect::check(cx, name, expr, recv2); }, @@ -2247,14 +2243,14 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio }, _ => {}, }, - ("count", []) => match method_call!(recv) { + ("count", []) => match method_call(recv) { Some((name @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => { iter_count::check(cx, expr, recv2, name); }, Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg), _ => {}, }, - ("expect", [_]) => match method_call!(recv) { + ("expect", [_]) => match method_call(recv) { Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv), _ => expect_used::check(cx, expr, recv), }, @@ -2271,13 +2267,13 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio flat_map_option::check(cx, expr, arg, span); }, ("flatten", []) => { - if let Some(("map", [recv, map_arg], _)) = method_call!(recv) { + if let Some(("map", [recv, map_arg], _)) = method_call(recv) { map_flatten::check(cx, expr, recv, map_arg); } }, ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span), ("for_each", [_]) => { - if let Some(("inspect", [_, _], span2)) = method_call!(recv) { + if let Some(("inspect", [_, _], span2)) = method_call(recv) { inspect_for_each::check(cx, expr, span2); } }, @@ -2286,7 +2282,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ("is_none", []) => check_is_some_is_none(cx, expr, recv, false), ("is_some", []) => check_is_some_is_none(cx, expr, recv, true), ("map", [m_arg]) => { - if let Some((name, [recv2, args @ ..], span2)) = method_call!(recv) { + if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) { match (name, args) { ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, msrv), ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, msrv), @@ -2301,7 +2297,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio }, ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map), ("next", []) => { - if let Some((name, [recv, args @ ..], _)) = method_call!(recv) { + if let Some((name, [recv, args @ ..], _)) = method_call(recv) { match (name, args) { ("filter", [arg]) => filter_next::check(cx, expr, recv, arg), ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv, arg, msrv), @@ -2312,7 +2308,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio } } }, - ("nth", [n_arg]) => match method_call!(recv) { + ("nth", [n_arg]) => match method_call(recv) { Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg), Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false), Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true), @@ -2344,12 +2340,12 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => { implicit_clone::check(cx, name, expr, recv, span); }, - ("unwrap", []) => match method_call!(recv) { + ("unwrap", []) => match method_call(recv) { Some(("get", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, false), Some(("get_mut", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, true), _ => unwrap_used::check(cx, expr, recv), }, - ("unwrap_or", [u_arg]) => match method_call!(recv) { + ("unwrap_or", [u_arg]) => match method_call(recv) { Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => { manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]); }, @@ -2358,7 +2354,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio }, _ => {}, }, - ("unwrap_or_else", [u_arg]) => match method_call!(recv) { + ("unwrap_or_else", [u_arg]) => match method_call(recv) { Some(("map", [recv, map_arg], _)) if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, msrv) => {}, _ => { unwrap_or_else_default::check(cx, expr, recv, u_arg); @@ -2371,7 +2367,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio } fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) { - if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call!(recv) { + if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call(recv) { search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span); } } @@ -2535,11 +2531,17 @@ impl SelfKind { implements_trait(cx, ty, trait_def_id, &[parent_ty.into()]) } + fn matches_none<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { + !matches_value(cx, parent_ty, ty) + && !matches_ref(cx, hir::Mutability::Not, parent_ty, ty) + && !matches_ref(cx, hir::Mutability::Mut, parent_ty, ty) + } + match self { Self::Value => matches_value(cx, parent_ty, ty), Self::Ref => matches_ref(cx, hir::Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty), Self::RefMut => matches_ref(cx, hir::Mutability::Mut, parent_ty, ty), - Self::No => ty != parent_ty, + Self::No => matches_none(cx, parent_ty, ty), } } 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 70f20da1d6d..514bdadc442 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs @@ -124,7 +124,7 @@ struct IterUsage { } #[allow(clippy::too_many_lines)] -fn parse_iter_usage( +fn parse_iter_usage<'tcx>( cx: &LateContext<'tcx>, ctxt: SyntaxContext, mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>, @@ -281,7 +281,7 @@ pub(super) fn check_needless_splitn( } } -fn check_iter( +fn check_iter<'tcx>( cx: &LateContext<'tcx>, ctxt: SyntaxContext, mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>, 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 8300df03e99..5999245ea7d 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 @@ -12,13 +12,13 @@ use rustc_span::{sym, Symbol}; use super::UNNECESSARY_TO_OWNED; -pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, receiver: &'tcx Expr<'tcx>) -> bool { +pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool { if_chain! { if let Some(parent) = get_parent_expr(cx, expr); if let Some(callee_def_id) = fn_def_id(cx, parent); if is_into_iter(cx, callee_def_id); then { - check_for_loop_iter(cx, parent, method_name, receiver) + check_for_loop_iter(cx, parent, method_name, receiver, false) } else { false } @@ -30,10 +30,11 @@ pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol /// include this code directly is so that it can be called from /// `unnecessary_into_owned::check_into_iter_call_arg`. pub fn check_for_loop_iter( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'tcx>, + cx: &LateContext<'_>, + expr: &Expr<'_>, method_name: Symbol, - receiver: &'tcx Expr<'tcx>, + receiver: &Expr<'_>, + cloned_before_iter: bool, ) -> bool { if_chain! { if let Some(grandparent) = get_parent_expr(cx, expr).and_then(|parent| get_parent_expr(cx, parent)); @@ -70,12 +71,22 @@ pub fn check_for_loop_iter( expr.span, &format!("unnecessary use of `{}`", method_name), |diag| { - diag.span_suggestion(expr.span, "use", snippet, Applicability::MachineApplicable); + // 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 + // incorrect. This is because the iterator that results from the call's removal + // could hold a reference to a resource that is used mutably. See + // https://github.com/rust-lang/rust-clippy/issues/8148. + let applicability = if cloned_before_iter { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }; + diag.span_suggestion(expr.span, "use", snippet, applicability); for addr_of_expr in addr_of_exprs { match addr_of_expr.kind { ExprKind::AddrOf(_, _, referent) => { let span = addr_of_expr.span.with_hi(referent.span.lo()); - diag.span_suggestion(span, "remove this `&`", String::new(), Applicability::MachineApplicable); + diag.span_suggestion(span, "remove this `&`", String::new(), applicability); } _ => unreachable!(), } @@ -90,7 +101,7 @@ pub fn check_for_loop_iter( /// The core logic of `check_for_loop_iter` above, this function wraps a use of /// `CloneOrCopyVisitor`. -fn clone_or_copy_needed( +fn clone_or_copy_needed<'tcx>( cx: &LateContext<'tcx>, pat: &Pat<'tcx>, body: &'tcx Expr<'tcx>, 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 c48bacfce0d..e5b6d296b2d 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 @@ -16,7 +16,7 @@ use std::cmp::max; use super::UNNECESSARY_TO_OWNED; -pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, args: &'tcx [Expr<'tcx>]) { +pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, args: &'tcx [Expr<'tcx>]) { if_chain! { if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let [receiver] = args; @@ -44,11 +44,11 @@ pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol /// call of a `to_owned`-like function is unnecessary. #[allow(clippy::too_many_lines)] fn check_addr_of_expr( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'tcx>, + cx: &LateContext<'_>, + expr: &Expr<'_>, method_name: Symbol, method_def_id: DefId, - receiver: &'tcx Expr<'tcx>, + receiver: &Expr<'_>, ) -> bool { if_chain! { if let Some(parent) = get_parent_expr(cx, expr); @@ -171,12 +171,7 @@ fn check_addr_of_expr( /// Checks whether `expr` is an argument in an `into_iter` call and, if so, determines whether its /// call of a `to_owned`-like function is unnecessary. -fn check_into_iter_call_arg( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'tcx>, - method_name: Symbol, - receiver: &'tcx Expr<'tcx>, -) -> bool { +fn check_into_iter_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool { if_chain! { if let Some(parent) = get_parent_expr(cx, expr); if let Some(callee_def_id) = fn_def_id(cx, parent); @@ -187,7 +182,13 @@ fn check_into_iter_call_arg( if let Some(item_ty) = get_iterator_item_ty(cx, parent_ty); if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); then { - if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver) { + if unnecessary_iter_cloned::check_for_loop_iter( + cx, + parent, + method_name, + receiver, + true, + ) { return true; } let cloned_or_copied = if is_copy(cx, item_ty) { @@ -195,6 +196,9 @@ fn check_into_iter_call_arg( } else { "cloned" }; + // The next suggestion may be incorrect because the removal of the `to_owned`-like + // function could cause the iterator to hold a reference to a resource that is used + // mutably. See https://github.com/rust-lang/rust-clippy/issues/8148. span_lint_and_sugg( cx, UNNECESSARY_TO_OWNED, @@ -202,7 +206,7 @@ fn check_into_iter_call_arg( &format!("unnecessary use of `{}`", method_name), "use", format!("{}.iter().{}()", receiver_snippet, cloned_or_copied), - Applicability::MachineApplicable, + Applicability::MaybeIncorrect, ); return true; } @@ -212,7 +216,7 @@ fn check_into_iter_call_arg( /// Checks whether `expr` is an argument in a function call and, if so, determines whether its call /// of a `to_owned`-like function is unnecessary. -fn check_other_call_arg( +fn check_other_call_arg<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, @@ -278,7 +282,7 @@ fn check_other_call_arg( /// Walks an expression's ancestors until it finds a non-`AddrOf` expression. Returns the first such /// expression found (if any) along with the immediately prior expression. -fn skip_addr_of_ancestors( +fn skip_addr_of_ancestors<'tcx>( cx: &LateContext<'tcx>, mut expr: &'tcx Expr<'tcx>, ) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { @@ -294,7 +298,7 @@ fn skip_addr_of_ancestors( /// Checks whether an expression is a function or method call and, if so, returns its `DefId`, /// `Substs`, and arguments. -fn get_callee_substs_and_args( +fn get_callee_substs_and_args<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, ) -> Option<(DefId, SubstsRef<'tcx>, &'tcx [Expr<'tcx>])> { @@ -319,7 +323,7 @@ fn get_callee_substs_and_args( } /// Returns the `TraitPredicate`s and `ProjectionPredicate`s for a function's input type. -fn get_input_traits_and_projections( +fn get_input_traits_and_projections<'tcx>( cx: &LateContext<'tcx>, callee_def_id: DefId, input: Ty<'tcx>, @@ -359,7 +363,11 @@ fn get_input_traits_and_projections( } /// Composes two substitutions by applying the latter to the types of the former. -fn compose_substs(cx: &LateContext<'tcx>, left: &[GenericArg<'tcx>], right: SubstsRef<'tcx>) -> Vec<GenericArg<'tcx>> { +fn compose_substs<'tcx>( + cx: &LateContext<'tcx>, + left: &[GenericArg<'tcx>], + right: SubstsRef<'tcx>, +) -> Vec<GenericArg<'tcx>> { left.iter() .map(|arg| { if let GenericArgKind::Type(arg_ty) = arg.unpack() { diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs index 401dc27811d..21b3f81d5d9 100644 --- a/src/tools/clippy/clippy_lints/src/misc.rs +++ b/src/tools/clippy/clippy_lints/src/misc.rs @@ -717,7 +717,7 @@ fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) } } -fn check_binary( +fn check_binary<'a>( cx: &LateContext<'a>, expr: &Expr<'_>, cmp: &rustc_span::source_map::Spanned<rustc_hir::BinOpKind>, 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 12e219cd5c8..842959ce36b 100644 --- a/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs +++ b/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::{higher, is_direct_expn_of}; +use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability}; use rustc_lint::{LateContext, LateLintPass}; @@ -34,26 +34,30 @@ declare_clippy_lint! { declare_lint_pass!(DebugAssertWithMutCall => [DEBUG_ASSERT_WITH_MUT_CALL]); -const DEBUG_MACRO_NAMES: [&str; 3] = ["debug_assert", "debug_assert_eq", "debug_assert_ne"]; - impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - for dmn in &DEBUG_MACRO_NAMES { - if is_direct_expn_of(e.span, dmn).is_some() { - if let Some(macro_args) = higher::extract_assert_macro_args(e) { - for arg in macro_args { - let mut visitor = MutArgVisitor::new(cx); - visitor.visit_expr(arg); - if let Some(span) = visitor.expr_span() { - span_lint( - cx, - DEBUG_ASSERT_WITH_MUT_CALL, - span, - &format!("do not call a function with mutable arguments inside of `{}!`", dmn), - ); - } - } - } + let Some(macro_call) = root_macro_call_first_node(cx, e) else { return }; + let macro_name = cx.tcx.item_name(macro_call.def_id); + if !matches!( + macro_name.as_str(), + "debug_assert" | "debug_assert_eq" | "debug_assert_ne" + ) { + return; + } + let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn) else { return }; + for arg in [lhs, rhs] { + let mut visitor = MutArgVisitor::new(cx); + visitor.visit_expr(arg); + if let Some(span) = visitor.expr_span() { + span_lint( + cx, + DEBUG_ASSERT_WITH_MUT_CALL, + span, + &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 816377fe65e..73823779e49 100644 --- a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs +++ b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs @@ -38,7 +38,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "pre 1.29.0"] pub MUTEX_ATOMIC, - perf, + nursery, "using a mutex where an atomic value could be used instead" } diff --git a/src/tools/clippy/clippy_lints/src/needless_for_each.rs b/src/tools/clippy/clippy_lints/src/needless_for_each.rs index 0c1da035173..19d58f7474b 100644 --- a/src/tools/clippy/clippy_lints/src/needless_for_each.rs +++ b/src/tools/clippy/clippy_lints/src/needless_for_each.rs @@ -48,7 +48,7 @@ declare_clippy_lint! { declare_lint_pass!(NeedlessForEach => [NEEDLESS_FOR_EACH]); -impl LateLintPass<'_> for NeedlessForEach { +impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { let expr = match stmt.kind { StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr, diff --git a/src/tools/clippy/clippy_lints/src/needless_late_init.rs b/src/tools/clippy/clippy_lints/src/needless_late_init.rs index 094a3f111ba..9957afcbf04 100644 --- a/src/tools/clippy/clippy_lints/src/needless_late_init.rs +++ b/src/tools/clippy/clippy_lints/src/needless_late_init.rs @@ -330,7 +330,7 @@ fn check<'tcx>( Some(()) } -impl LateLintPass<'tcx> for NeedlessLateInit { +impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { let mut parents = cx.tcx.hir().parent_iter(local.hir_id); diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index 9d5babc5de8..5bf8a1ba1ca 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -82,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect { } } -fn check_no_effect(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> bool { +fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { if let StmtKind::Semi(expr) = stmt.kind { if has_no_effect(cx, expr) { span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect"); @@ -155,7 +155,7 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } } -fn check_unnecessary_operation(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { +fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { if_chain! { if let StmtKind::Semi(expr) = stmt.kind; if let Some(reduced) = reduce_expression(cx, expr); 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 074ba9e92ba..7d2ff083b7e 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -12,11 +12,10 @@ use rustc_hir::def_id::DefId; use rustc_hir::{ BodyId, Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp, }; -use rustc_infer::traits::specialization_graph; use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_middle::mir::interpret::{ConstValue, ErrorHandled}; use rustc_middle::ty::adjustment::Adjust; -use rustc_middle::ty::{self, AssocKind, Const, Ty}; +use rustc_middle::ty::{self, Const, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{InnerSpan, Span, DUMMY_SP}; use rustc_typeck::hir_ty_to_ty; @@ -293,8 +292,10 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { // Lint a trait impl item only when the definition is a generic type, // assuming an assoc const is not meant to be an interior mutable type. if let Some(of_trait_def_id) = of_trait_ref.trait_def_id(); - if let Some(of_assoc_item) = specialization_graph::Node::Trait(of_trait_def_id) - .item(cx.tcx, impl_item.ident, AssocKind::Const, of_trait_def_id); + if let Some(of_assoc_item) = cx + .tcx + .associated_item(impl_item.def_id) + .trait_item_def_id; if cx .tcx .layout_of(cx.tcx.param_env(of_trait_def_id).and( @@ -303,7 +304,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { // and, in that case, the definition is *not* generic. cx.tcx.normalize_erasing_regions( cx.tcx.param_env(of_trait_def_id), - cx.tcx.type_of(of_assoc_item.def_id), + cx.tcx.type_of(of_assoc_item), ), )) .is_err(); diff --git a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs index 4b57dbc4c41..e46fee4cac5 100644 --- a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs +++ b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs @@ -40,7 +40,7 @@ declare_clippy_lint! { declare_lint_pass!(NonOctalUnixPermissions => [NON_OCTAL_UNIX_PERMISSIONS]); -impl LateLintPass<'_> for NonOctalUnixPermissions { +impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { match &expr.kind { ExprKind::MethodCall(path, _, [func, param], _) => { diff --git a/src/tools/clippy/clippy_lints/src/octal_escapes.rs b/src/tools/clippy/clippy_lints/src/octal_escapes.rs index 9c971437645..e0da12f77fc 100644 --- a/src/tools/clippy/clippy_lints/src/octal_escapes.rs +++ b/src/tools/clippy/clippy_lints/src/octal_escapes.rs @@ -50,7 +50,7 @@ declare_clippy_lint! { declare_lint_pass!(OctalEscapes => [OCTAL_ESCAPES]); impl EarlyLintPass for OctalEscapes { - fn check_expr(&mut self, cx: &EarlyContext<'tcx>, expr: &Expr) { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if in_external_macro(cx.sess, expr.span) { return; } @@ -65,7 +65,7 @@ impl EarlyLintPass for OctalEscapes { } } -fn check_lit(cx: &EarlyContext<'tcx>, lit: &Lit, span: Span, is_string: bool) { +fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) { let contents = lit.symbol.as_str(); let mut iter = contents.char_indices().peekable(); let mut found = vec![]; diff --git a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs index 8769c045214..b7a56970b33 100644 --- a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs +++ b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs @@ -1,8 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::root_macro_call_first_node; +use clippy_utils::return_ty; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{find_macro_calls, is_expn_of, return_ty}; +use clippy_utils::visitors::expr_visitor_no_bodies; use rustc_hir as hir; -use rustc_hir::intravisit::FnKind; +use rustc_hir::intravisit::{FnKind, Visitor}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span}; @@ -55,19 +57,19 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { } fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) { - let mut panics = find_macro_calls( - &[ - "unimplemented", - "unreachable", - "panic", - "todo", - "assert", - "assert_eq", - "assert_ne", - ], - body, - ); - panics.retain(|span| is_expn_of(*span, "debug_assert").is_none()); + let mut panics = Vec::new(); + expr_visitor_no_bodies(|expr| { + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return true }; + if matches!( + &*cx.tcx.item_name(macro_call.def_id).as_str(), + "unimplemented" | "unreachable" | "panic" | "todo" | "assert" | "assert_eq" | "assert_ne" + ) { + panics.push(macro_call.span); + return false; + } + true + }) + .visit_expr(&body.value); if !panics.is_empty() { span_lint_and_then( cx, diff --git a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs index edfac824ded..6ef6b9a20aa 100644 --- a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs +++ b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs @@ -1,10 +1,8 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::{is_expn_of, match_panic_call}; -use if_chain::if_chain; +use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -78,37 +76,37 @@ declare_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANI impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if match_panic_call(cx, expr).is_some() - && (is_expn_of(expr.span, "debug_assert").is_none() && is_expn_of(expr.span, "assert").is_none()) - { - let span = get_outer_span(expr); - if is_expn_of(expr.span, "unimplemented").is_some() { + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; + if is_panic(cx, macro_call.def_id) { + span_lint( + cx, + PANIC, + macro_call.span, + "`panic` should not be present in production code", + ); + return; + } + match cx.tcx.item_name(macro_call.def_id).as_str() { + "todo" => { + span_lint( + cx, + TODO, + macro_call.span, + "`todo` should not be present in production code", + ); + }, + "unimplemented" => { span_lint( cx, UNIMPLEMENTED, - span, + macro_call.span, "`unimplemented` should not be present in production code", ); - } else if is_expn_of(expr.span, "todo").is_some() { - span_lint(cx, TODO, span, "`todo` should not be present in production code"); - } else if is_expn_of(expr.span, "unreachable").is_some() { - span_lint(cx, UNREACHABLE, span, "usage of the `unreachable!` macro"); - } else if is_expn_of(expr.span, "panic").is_some() { - span_lint(cx, PANIC, span, "`panic` should not be present in production code"); - } - } - } -} - -fn get_outer_span(expr: &Expr<'_>) -> Span { - if_chain! { - if expr.span.from_expansion(); - let first = expr.span.ctxt().outer_expn_data().call_site; - if first.from_expansion(); - then { - first.ctxt().outer_expn_data().call_site - } else { - expr.span + }, + "unreachable" => { + span_lint(cx, UNREACHABLE, macro_call.span, "usage of the `unreachable!` macro"); + }, + _ => {}, } } } diff --git a/src/tools/clippy/clippy_lints/src/ptr_eq.rs b/src/tools/clippy/clippy_lints/src/ptr_eq.rs index 3c126fc1ca6..2bec93ac606 100644 --- a/src/tools/clippy/clippy_lints/src/ptr_eq.rs +++ b/src/tools/clippy/clippy_lints/src/ptr_eq.rs @@ -39,7 +39,7 @@ declare_lint_pass!(PtrEq => [PTR_EQ]); static LINT_MSG: &str = "use `std::ptr::eq` when comparing raw pointers"; -impl LateLintPass<'_> for PtrEq { +impl<'tcx> LateLintPass<'tcx> for PtrEq { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if expr.span.from_expansion() { return; diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs index 1cf349f8aa7..1991a01fb60 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs @@ -220,7 +220,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { continue; } else if let Some(loc) = clone_usage.cloned_consume_or_mutate_loc { // cloned value is mutated, and the clone is alive. - if possible_borrower.is_alive_at(ret_local, loc) { + if possible_borrower.local_is_alive_at(ret_local, loc) { continue; } } @@ -767,7 +767,7 @@ impl PossibleBorrowerMap<'_, '_> { self.bitset.0 == self.bitset.1 } - fn is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool { + fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool { self.maybe_live.seek_after_primary_effect(at); self.maybe_live.contains(local) } diff --git a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs index b2bd0103d11..7c88b42ea31 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs @@ -42,7 +42,7 @@ declare_clippy_lint! { declare_lint_pass!(RedundantSlicing => [REDUNDANT_SLICING]); -impl LateLintPass<'_> for RedundantSlicing { +impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if expr.span.from_expansion() { return; diff --git a/src/tools/clippy/clippy_lints/src/reference.rs b/src/tools/clippy/clippy_lints/src/reference.rs index 22ae7a291d0..b2448372370 100644 --- a/src/tools/clippy/clippy_lints/src/reference.rs +++ b/src/tools/clippy/clippy_lints/src/reference.rs @@ -50,6 +50,7 @@ impl EarlyLintPass for DerefAddrOf { if_chain! { if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind; if let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind; + if deref_target.span.ctxt() == e.span.ctxt(); if !addrof_target.span.from_expansion(); then { let mut applicability = Applicability::MachineApplicable; diff --git a/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs b/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs index b57ec96bc7e..5dafd08cf3b 100644 --- a/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs +++ b/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs @@ -1,5 +1,6 @@ +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_must_use_ty; -use clippy_utils::{diagnostics::span_lint, nth_arg, return_ty}; +use clippy_utils::{nth_arg, return_ty}; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, HirId, TraitItem, TraitItemKind}; @@ -13,25 +14,46 @@ declare_clippy_lint! { /// This lint warns when a method returning `Self` doesn't have the `#[must_use]` attribute. /// /// ### Why is this bad? - /// It prevents to "forget" to use the newly created value. + /// Methods returning `Self` often create new values, having the `#[must_use]` attribute + /// prevents users from "forgetting" to use the newly created value. + /// + /// The `#[must_use]` attribute can be added to the type itself to ensure that instances + /// are never forgotten. Functions returning a type marked with `#[must_use]` will not be + /// linted, as the usage is already enforced by the type attribute. /// /// ### Limitations /// This lint is only applied on methods taking a `self` argument. It would be mostly noise /// if it was added on constructors for example. /// /// ### Example + /// Missing attribute /// ```rust /// pub struct Bar; - /// /// impl Bar { /// // Bad /// pub fn bar(&self) -> Self { /// Self /// } + /// } + /// ``` /// - /// // Good + /// It's better to have the `#[must_use]` attribute on the method like this: + /// ```rust + /// pub struct Bar; + /// impl Bar { /// #[must_use] - /// pub fn foo(&self) -> Self { + /// pub fn bar(&self) -> Self { + /// Self + /// } + /// } + /// ``` + /// + /// Or on the type definition like this: + /// ```rust + /// #[must_use] + /// pub struct Bar; + /// impl Bar { + /// pub fn bar(&self) -> Self { /// Self /// } /// } @@ -44,7 +66,7 @@ declare_clippy_lint! { declare_lint_pass!(ReturnSelfNotMustUse => [RETURN_SELF_NOT_MUST_USE]); -fn check_method(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'tcx>, fn_def: LocalDefId, span: Span, hir_id: HirId) { +fn check_method(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_def: LocalDefId, span: Span, hir_id: HirId) { if_chain! { // If it comes from an external macro, better ignore it. if !in_external_macro(cx.sess(), span); @@ -65,11 +87,13 @@ fn check_method(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'tcx>, fn_def: LocalD if !is_must_use_ty(cx, ret_ty); then { - span_lint( + span_lint_and_help( cx, RETURN_SELF_NOT_MUST_USE, span, "missing `#[must_use]` attribute on a method returning `Self`", + None, + "consider adding the `#[must_use]` attribute to the method or directly to the `Self` type" ); } } diff --git a/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs b/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs index 0b3bbbc8155..729694da46d 100644 --- a/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs +++ b/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs @@ -37,7 +37,7 @@ declare_clippy_lint! { declare_lint_pass!(SemicolonIfNothingReturned => [SEMICOLON_IF_NOTHING_RETURNED]); -impl LateLintPass<'_> for SemicolonIfNothingReturned { +impl<'tcx> LateLintPass<'tcx> for SemicolonIfNothingReturned { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { if_chain! { if !block.span.from_expansion(); diff --git a/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs b/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs new file mode 100644 index 00000000000..ee82666b5af --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs @@ -0,0 +1,63 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_ast::ast::{GenericParam, GenericParamKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for lifetimes with names which are one character + /// long. + /// + /// ### Why is this bad? + /// A single character is likely not enough to express the + /// purpose of a lifetime. Using a longer name can make code + /// easier to understand, especially for those who are new to + /// Rust. + /// + /// ### Known problems + /// Rust programmers and learning resources tend to use single + /// character lifetimes, so this lint is at odds with the + /// ecosystem at large. In addition, the lifetime's purpose may + /// be obvious or, rarely, expressible in one character. + /// + /// ### Example + /// ```rust + /// struct DiagnosticCtx<'a> { + /// source: &'a str, + /// } + /// ``` + /// Use instead: + /// ```rust + /// struct DiagnosticCtx<'src> { + /// source: &'src str, + /// } + /// ``` + #[clippy::version = "1.59.0"] + pub SINGLE_CHAR_LIFETIME_NAMES, + restriction, + "warns against single-character lifetime names" +} + +declare_lint_pass!(SingleCharLifetimeNames => [SINGLE_CHAR_LIFETIME_NAMES]); + +impl EarlyLintPass for SingleCharLifetimeNames { + fn check_generic_param(&mut self, ctx: &EarlyContext<'_>, param: &GenericParam) { + if in_external_macro(ctx.sess, param.ident.span) { + return; + } + + if let GenericParamKind::Lifetime = param.kind { + if !param.is_placeholder && param.ident.as_str().len() <= 2 { + span_lint_and_help( + ctx, + SINGLE_CHAR_LIFETIME_NAMES, + param.ident.span, + "single-character lifetime names are likely uninformative", + None, + "use a more informative name", + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs index df1e85afdd7..9b195f3c0a2 100644 --- a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs +++ b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs @@ -37,7 +37,7 @@ declare_clippy_lint! { declare_lint_pass!(SizeOfInElementCount => [SIZE_OF_IN_ELEMENT_COUNT]); -fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) -> Option<Ty<'tcx>> { +fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) -> Option<Ty<'tcx>> { match expr.kind { ExprKind::Call(count_func, _func_args) => { if_chain! { @@ -64,7 +64,10 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) } } -fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { +fn get_pointee_ty_and_count_expr<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, +) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { const FUNCTIONS: [&[&str]; 8] = [ &paths::PTR_COPY_NONOVERLAPPING, &paths::PTR_COPY, diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index ad8e72ad764..b4a71aefd43 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -381,7 +381,7 @@ declare_clippy_lint! { declare_lint_pass!(StrToString => [STR_TO_STRING]); -impl LateLintPass<'_> for StrToString { +impl<'tcx> LateLintPass<'tcx> for StrToString { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { if_chain! { if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind; @@ -431,7 +431,7 @@ declare_clippy_lint! { declare_lint_pass!(StringToString => [STRING_TO_STRING]); -impl LateLintPass<'_> for StringToString { +impl<'tcx> LateLintPass<'tcx> for StringToString { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { if_chain! { if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind; diff --git a/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs b/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs index fee01fb0bd1..d6e948a7560 100644 --- a/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs +++ b/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs @@ -39,7 +39,7 @@ declare_clippy_lint! { declare_lint_pass!(StrlenOnCStrings => [STRLEN_ON_C_STRINGS]); -impl LateLintPass<'tcx> for StrlenOnCStrings { +impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { if !expr.span.from_expansion(); diff --git a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs index faf43fd9fc1..ca725918e87 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs @@ -355,7 +355,7 @@ struct BinaryOp<'exprs> { right: &'exprs Expr, } -impl BinaryOp<'exprs> { +impl<'exprs> BinaryOp<'exprs> { fn new(op: BinOpKind, span: Span, (left, right): (&'exprs Expr, &'exprs Expr)) -> Self { Self { op, span, left, right } } @@ -419,7 +419,7 @@ fn chained_binops(kind: &ExprKind) -> Option<Vec<BinaryOp<'_>>> { } } -fn chained_binops_helper(left_outer: &'expr Expr, right_outer: &'expr Expr) -> Option<Vec<BinaryOp<'expr>>> { +fn chained_binops_helper<'expr>(left_outer: &'expr Expr, right_outer: &'expr Expr) -> Option<Vec<BinaryOp<'expr>>> { match (&left_outer.kind, &right_outer.kind) { ( ExprKind::Paren(ref left_e) | ExprKind::Unary(_, ref left_e), 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 af36f726700..c9b2ce476e8 100644 --- a/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs +++ b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs @@ -53,13 +53,12 @@ impl<'tcx> LateLintPass<'tcx> for TrailingEmptyArray { } } -fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) -> bool { +fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'_>, item: &Item<'_>) -> bool { if_chain! { // First check if last field is an array if let ItemKind::Struct(data, _) = &item.kind; if let Some(last_field) = data.fields().last(); - if let rustc_hir::TyKind::Array(_, length) = last_field.ty.kind; - if let rustc_hir::ArrayLen::Body(length) = length; + if let rustc_hir::TyKind::Array(_, rustc_hir::ArrayLen::Body(length)) = last_field.ty.kind; // Then check if that that array zero-sized let length_ldid = cx.tcx.hir().local_def_id(length.hir_id); diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs index fb4abceac25..6369aafe3f9 100644 --- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs @@ -1,11 +1,16 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::{snippet, snippet_with_applicability}; -use clippy_utils::SpanlessHash; +use clippy_utils::{SpanlessEq, SpanlessHash}; +use core::hash::{Hash, Hasher}; use if_chain::if_chain; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::unhash::UnhashMap; use rustc_errors::Applicability; -use rustc_hir::{def::Res, GenericBound, Generics, ParamName, Path, QPath, TyKind, WherePredicate}; +use rustc_hir::def::Res; +use rustc_hir::{ + GenericBound, Generics, Item, ItemKind, Node, ParamName, Path, PathSegment, QPath, TraitItem, Ty, TyKind, + WherePredicate, +}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; @@ -83,6 +88,53 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { self.check_type_repetition(cx, gen); check_trait_bound_duplication(cx, gen); } + + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'tcx>) { + let Generics { where_clause, .. } = &item.generics; + let mut self_bounds_set = FxHashSet::default(); + + for predicate in where_clause.predicates { + if_chain! { + if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate; + if !bound_predicate.span.from_expansion(); + if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind; + if let Some(PathSegment { res: Some(Res::SelfTy(Some(def_id), _)), .. }) = segments.first(); + + if let Some( + Node::Item( + Item { + kind: ItemKind::Trait(_, _, _, self_bounds, _), + .. } + ) + ) = cx.tcx.hir().get_if_local(*def_id); + then { + if self_bounds_set.is_empty() { + for bound in self_bounds.iter() { + let Some((self_res, _)) = get_trait_res_span_from_bound(bound) else { continue }; + self_bounds_set.insert(self_res); + } + } + + bound_predicate + .bounds + .iter() + .filter_map(get_trait_res_span_from_bound) + .for_each(|(trait_item_res, span)| { + if self_bounds_set.get(&trait_item_res).is_some() { + span_lint_and_help( + cx, + TRAIT_DUPLICATION_IN_BOUNDS, + span, + "this trait bound is already specified in trait declaration", + None, + "consider removing this trait bound", + ); + } + }); + } + } + } + } } fn get_trait_res_span_from_bound(bound: &GenericBound<'_>) -> Option<(Res, Span)> { @@ -94,24 +146,40 @@ fn get_trait_res_span_from_bound(bound: &GenericBound<'_>) -> Option<(Res, Span) } impl TraitBounds { - fn check_type_repetition(self, cx: &LateContext<'_>, gen: &'_ Generics<'_>) { + fn check_type_repetition<'tcx>(self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) { + struct SpanlessTy<'cx, 'tcx> { + ty: &'tcx Ty<'tcx>, + cx: &'cx LateContext<'tcx>, + } + impl PartialEq for SpanlessTy<'_, '_> { + fn eq(&self, other: &Self) -> bool { + let mut eq = SpanlessEq::new(self.cx); + eq.inter_expr().eq_ty(self.ty, other.ty) + } + } + impl Hash for SpanlessTy<'_, '_> { + fn hash<H: Hasher>(&self, h: &mut H) { + let mut t = SpanlessHash::new(self.cx); + t.hash_ty(self.ty); + h.write_u64(t.finish()); + } + } + impl Eq for SpanlessTy<'_, '_> {} + if gen.span.from_expansion() { return; } - let hash = |ty| -> u64 { - let mut hasher = SpanlessHash::new(cx); - hasher.hash_ty(ty); - hasher.finish() - }; - let mut map: UnhashMap<u64, Vec<&GenericBound<'_>>> = UnhashMap::default(); + let mut map: UnhashMap<SpanlessTy<'_, '_>, Vec<&GenericBound<'_>>> = UnhashMap::default(); let mut applicability = Applicability::MaybeIncorrect; for bound in gen.where_clause.predicates { if_chain! { if let WherePredicate::BoundPredicate(ref p) = bound; if p.bounds.len() as u64 <= self.max_trait_bounds; if !p.span.from_expansion(); - let h = hash(p.bounded_ty); - if let Some(ref v) = map.insert(h, p.bounds.iter().collect::<Vec<_>>()); + if let Some(ref v) = map.insert( + SpanlessTy { ty: p.bounded_ty, cx }, + p.bounds.iter().collect::<Vec<_>>() + ); then { let mut hint_string = format!( diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs index 481e5957435..9d57505e55e 100644 --- a/src/tools/clippy/clippy_lints/src/types/mod.rs +++ b/src/tools/clippy/clippy_lints/src/types/mod.rs @@ -167,8 +167,9 @@ declare_clippy_lint! { /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information. /// /// ### Why is this bad? - /// Any `&Box<T>` can also be a `&T`, which is more - /// general. + /// A `&Box<T>` parameter requires the function caller to box `T` first before passing it to a function. + /// Using `&T` defines a concrete type for the parameter and generalizes the function, this would also + /// auto-deref to `&T` at the function call site if passed a `&Box<T>`. /// /// ### Example /// ```rust,ignore 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 3d3b4a6679d..697ed267e2f 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -113,8 +113,8 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks { } } -impl<'hir> Visitor<'hir> for UndocumentedUnsafeBlocks { - type Map = Map<'hir>; +impl<'v> Visitor<'v> for UndocumentedUnsafeBlocks { + type Map = Map<'v>; fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { NestedVisitorMap::None diff --git a/src/tools/clippy/clippy_lints/src/undropped_manually_drops.rs b/src/tools/clippy/clippy_lints/src/undropped_manually_drops.rs index c58fa67a023..7557e14d11f 100644 --- a/src/tools/clippy/clippy_lints/src/undropped_manually_drops.rs +++ b/src/tools/clippy/clippy_lints/src/undropped_manually_drops.rs @@ -36,7 +36,7 @@ declare_clippy_lint! { declare_lint_pass!(UndroppedManuallyDrops => [UNDROPPED_MANUALLY_DROPS]); -impl LateLintPass<'tcx> for UndroppedManuallyDrops { +impl<'tcx> LateLintPass<'tcx> for UndroppedManuallyDrops { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let Some([arg_0, ..]) = match_function_call(cx, expr, &paths::DROP) { let ty = cx.typeck_results().expr_ty(arg_0); diff --git a/src/tools/clippy/clippy_lints/src/uninit_vec.rs b/src/tools/clippy/clippy_lints/src/uninit_vec.rs index 46cc76b150e..2ffaf24f942 100644 --- a/src/tools/clippy/clippy_lints/src/uninit_vec.rs +++ b/src/tools/clippy/clippy_lints/src/uninit_vec.rs @@ -78,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for UninitVec { } } -fn handle_uninit_vec_pair( +fn handle_uninit_vec_pair<'tcx>( cx: &LateContext<'tcx>, maybe_init_or_reserve: &'tcx Stmt<'tcx>, maybe_set_len: &'tcx Expr<'tcx>, @@ -196,7 +196,7 @@ fn is_reserve(cx: &LateContext<'_>, path: &PathSegment<'_>, self_expr: &Expr<'_> } /// Returns self if the expression is `Vec::set_len()` -fn extract_set_len_self(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(&'tcx Expr<'tcx>, Span)> { +fn extract_set_len_self<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(&'tcx Expr<'tcx>, Span)> { // peel unsafe blocks in `unsafe { vec.set_len() }` let expr = peel_hir_expr_while(expr, |e| { if let ExprKind::Block(block, _) = e.kind { diff --git a/src/tools/clippy/clippy_lints/src/unit_hash.rs b/src/tools/clippy/clippy_lints/src/unit_hash.rs index 26b4e0f58a8..dcf8a9d7c84 100644 --- a/src/tools/clippy/clippy_lints/src/unit_hash.rs +++ b/src/tools/clippy/clippy_lints/src/unit_hash.rs @@ -46,7 +46,7 @@ declare_clippy_lint! { } declare_lint_pass!(UnitHash => [UNIT_HASH]); -impl LateLintPass<'tcx> for UnitHash { +impl<'tcx> LateLintPass<'tcx> for UnitHash { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if_chain! { if let ExprKind::MethodCall(name_ident, _, args, _) = &expr.kind; 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 6d9aff47421..1dd8895ebd0 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 @@ -1,35 +1,29 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_span::hygiene::{ExpnKind, MacroKind}; use super::UNIT_CMP; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if expr.span.from_expansion() { - if let Some(callee) = expr.span.source_callee() { - if let ExpnKind::Macro(MacroKind::Bang, symbol) = callee.kind { - if let ExprKind::Binary(ref cmp, left, _) = expr.kind { - let op = cmp.node; - if op.is_comparison() && cx.typeck_results().expr_ty(left).is_unit() { - let result = match symbol.as_str() { - "assert_eq" | "debug_assert_eq" => "succeed", - "assert_ne" | "debug_assert_ne" => "fail", - _ => return, - }; - span_lint( - cx, - UNIT_CMP, - expr.span, - &format!( - "`{}` of unit values detected. This will always {}", - symbol.as_str(), - result - ), - ); - } - } + if let Some(macro_call) = root_macro_call_first_node(cx, expr) { + let macro_name = cx.tcx.item_name(macro_call.def_id); + let result = match macro_name.as_str() { + "assert_eq" | "debug_assert_eq" => "succeed", + "assert_ne" | "debug_assert_ne" => "fail", + _ => return, + }; + let Some ((left, _, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return }; + if !cx.typeck_results().expr_ty(left).is_unit() { + return; } + span_lint( + cx, + UNIT_CMP, + macro_call.span, + &format!("`{}` of unit values detected. This will always {}", macro_name, result), + ); } return; } 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 004530db086..287ac5b4a90 100644 --- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs +++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::{is_try, match_trait_method, paths}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -17,10 +17,17 @@ declare_clippy_lint! { /// partial-write/read, use /// `write_all`/`read_exact` instead. /// + /// When working with asynchronous code (either with the `futures` + /// crate or with `tokio`), a similar issue exists for + /// `AsyncWriteExt::write()` and `AsyncReadExt::read()` : these + /// functions are also not guaranteed to process the entire + /// buffer. Your code should either handle partial-writes/reads, or + /// call the `write_all`/`read_exact` methods on those traits instead. + /// /// ### Known problems /// Detects only common patterns. /// - /// ### Example + /// ### Examples /// ```rust,ignore /// use std::io; /// fn foo<W: io::Write>(w: &mut W) -> io::Result<()> { @@ -68,6 +75,23 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { } } +/// If `expr` is an (e).await, return the inner expression "e" that's being +/// waited on. Otherwise return None. +fn try_remove_await<'a>(expr: &'a hir::Expr<'a>) -> Option<&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 matches!( + func.kind, + hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoFutureIntoFuture, ..)) + ) { + return Some(arg_0); + } + } + } + + None +} + fn check_map_error(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) { let mut call = call; while let hir::ExprKind::MethodCall(path, _, args, _) = call.kind { @@ -77,30 +101,69 @@ fn check_map_error(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr< break; } } - check_method_call(cx, call, expr); + + if let Some(call) = try_remove_await(call) { + check_method_call(cx, call, expr, true); + } else { + check_method_call(cx, call, expr, false); + } } -fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) { +fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>, is_await: bool) { if let hir::ExprKind::MethodCall(path, _, _, _) = call.kind { let symbol = path.ident.as_str(); - let read_trait = match_trait_method(cx, call, &paths::IO_READ); - let write_trait = match_trait_method(cx, call, &paths::IO_WRITE); + let read_trait = if is_await { + match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCREADEXT) + || match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCREADEXT) + } else { + match_trait_method(cx, call, &paths::IO_READ) + }; + let write_trait = if is_await { + match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCWRITEEXT) + || match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCWRITEEXT) + } else { + match_trait_method(cx, call, &paths::IO_WRITE) + }; - match (read_trait, write_trait, symbol) { - (true, _, "read") => span_lint( + match (read_trait, write_trait, symbol, is_await) { + (true, _, "read", false) => span_lint_and_help( + cx, + UNUSED_IO_AMOUNT, + expr.span, + "read amount is not handled", + None, + "use `Read::read_exact` instead, or handle partial reads", + ), + (true, _, "read", true) => span_lint_and_help( cx, UNUSED_IO_AMOUNT, expr.span, - "read amount is not handled. Use `Read::read_exact` instead", + "read amount is not handled", + None, + "use `AsyncReadExt::read_exact` instead, or handle partial reads", ), - (true, _, "read_vectored") => span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "read amount is not handled"), - (_, true, "write") => span_lint( + (true, _, "read_vectored", _) => { + span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "read amount is not handled"); + }, + (_, true, "write", false) => span_lint_and_help( cx, UNUSED_IO_AMOUNT, expr.span, - "written amount is not handled. Use `Write::write_all` instead", + "written amount is not handled", + None, + "use `Write::write_all` instead, or handle partial writes", ), - (_, true, "write_vectored") => span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "written amount is not handled"), + (_, true, "write", true) => span_lint_and_help( + cx, + UNUSED_IO_AMOUNT, + expr.span, + "written amount is not handled", + None, + "use `AsyncWriteExt::write_all` instead, or handle partial writes", + ), + (_, true, "write_vectored", _) => { + span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "written amount is not handled"); + }, _ => (), } } diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs index 059f7f647f8..a86db58741e 100644 --- a/src/tools/clippy/clippy_lints/src/use_self.rs +++ b/src/tools/clippy/clippy_lints/src/use_self.rs @@ -13,7 +13,6 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; -use rustc_middle::ty::AssocKind; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; @@ -143,10 +142,10 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { // trait, not in the impl of the trait. let trait_method = cx .tcx - .associated_items(impl_trait_ref.def_id) - .find_by_name_and_kind(cx.tcx, impl_item.ident, AssocKind::Fn, impl_trait_ref.def_id) + .associated_item(impl_item.def_id) + .trait_item_def_id .expect("impl method matches a trait method"); - let trait_method_sig = cx.tcx.fn_sig(trait_method.def_id); + let trait_method_sig = cx.tcx.fn_sig(trait_method); let trait_method_sig = cx.tcx.erase_late_bound_regions(trait_method_sig); // `impl_inputs_outputs` is an iterator over the types (`hir::Ty`) declared in the diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 9b06ca4e824..7751c593e43 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -573,7 +573,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { bind!(self, anon_const); out!("if let ArrayLen::Body({anon_const}) = {length};"); self.body(field!(anon_const.body)); - } + }, } }, ExprKind::Err => kind!("Err"), diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs index 9c83d30eb9c..d6deb50cc90 100644 --- a/src/tools/clippy/clippy_lints/src/utils/conf.rs +++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs @@ -23,6 +23,14 @@ pub enum DisallowedMethod { WithReason { path: String, reason: Option<String> }, } +impl DisallowedMethod { + pub fn path(&self) -> &str { + let (Self::Simple(path) | Self::WithReason { path, .. }) = self; + + path + } +} + /// A single disallowed type, used by the `DISALLOWED_TYPES` lint. #[derive(Clone, Debug, Deserialize)] #[serde(untagged)] @@ -113,7 +121,7 @@ macro_rules! define_Conf { } } - #[cfg(feature = "metadata-collector-lint")] + #[cfg(feature = "internal")] pub mod metadata { use crate::utils::internal_lints::metadata_collector::ClippyConfiguration; diff --git a/src/tools/clippy/clippy_lints/src/utils/inspector.rs b/src/tools/clippy/clippy_lints/src/utils/inspector.rs index c96766e5678..e90b6b73b34 100644 --- a/src/tools/clippy/clippy_lints/src/utils/inspector.rs +++ b/src/tools/clippy/clippy_lints/src/utils/inspector.rs @@ -342,8 +342,8 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { match length { hir::ArrayLen::Infer(_, _) => println!("{}repeat count: _", ind), hir::ArrayLen::Body(anon_const) => { - print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1) - } + print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1); + }, } }, hir::ExprKind::Err => { diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs index e98dcd3cf98..9c3dcc8e96a 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs @@ -1,5 +1,6 @@ use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::snippet; use clippy_utils::ty::match_type; use clippy_utils::{ @@ -34,7 +35,7 @@ use rustc_typeck::hir_ty_to_ty; use std::borrow::{Borrow, Cow}; -#[cfg(feature = "metadata-collector-lint")] +#[cfg(feature = "internal")] pub mod metadata_collector; declare_clippy_lint! { @@ -410,9 +411,13 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { } self.declared_lints.insert(item.ident.name, item.span); } - } else if is_expn_of(item.span, "impl_lint_pass").is_some() - || is_expn_of(item.span, "declare_lint_pass").is_some() - { + } else if let Some(macro_call) = root_macro_call_first_node(cx, item) { + if !matches!( + &*cx.tcx.item_name(macro_call.def_id).as_str(), + "impl_lint_pass" | "declare_lint_pass" + ) { + return; + } if let hir::ItemKind::Impl(hir::Impl { of_trait: None, items: impl_item_refs, @@ -924,9 +929,20 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { let lang_item_path = cx.get_def_path(*item_def_id); if path_syms.starts_with(&lang_item_path) { if let [item] = &path_syms[lang_item_path.len()..] { - for child in cx.tcx.item_children(*item_def_id) { - if child.ident.name == *item { - return true; + if matches!( + cx.tcx.def_kind(*item_def_id), + DefKind::Mod | DefKind::Enum | DefKind::Trait + ) { + for child in cx.tcx.module_children(*item_def_id) { + if child.ident.name == *item { + return true; + } + } + } else { + for child in cx.tcx.associated_item_def_ids(*item_def_id) { + if cx.tcx.item_name(*child) == *item { + return true; + } } } } @@ -984,7 +1000,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { if let Some(def_id) = path_to_res(cx, module).opt_def_id() { - for item in cx.tcx.item_children(def_id).iter() { + for item in cx.tcx.module_children(def_id).iter() { if_chain! { if let Res::Def(DefKind::Const, item_def_id) = item.res; let ty = cx.tcx.type_of(item_def_id); 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 7707eebd622..4e46d79dc08 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 @@ -1,8 +1,7 @@ //! This lint is used to collect metadata about clippy lints. This metadata is exported as a json //! file and then used to generate the [clippy lint list](https://rust-lang.github.io/rust-clippy/master/index.html) //! -//! This module and therefor the entire lint is guarded by a feature flag called -//! `metadata-collector-lint` +//! This module and therefore the entire lint is guarded by a feature flag called `internal` //! //! The module transforms all lint names to ascii lowercase to ensure that we don't have mismatches //! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such @@ -578,7 +577,7 @@ fn get_lint_version(cx: &LateContext<'_>, item: &Item<'_>) -> String { fn get_lint_group_and_level_or_lint( cx: &LateContext<'_>, lint_name: &str, - item: &'hir Item<'_>, + item: &Item<'_>, ) -> Option<(String, &'static str)> { let result = cx .lint_store @@ -697,20 +696,20 @@ fn extract_emission_info<'hir>( } /// Resolves the possible lints that this expression could reference -fn resolve_lints(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Vec<String> { +fn resolve_lints<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Vec<String> { let mut resolver = LintResolver::new(cx); resolver.visit_expr(expr); resolver.lints } /// This function tries to resolve the linked applicability to the given expression. -fn resolve_applicability(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<usize> { +fn resolve_applicability<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<usize> { let mut resolver = ApplicabilityResolver::new(cx); resolver.visit_expr(expr); resolver.complete() } -fn check_is_multi_part(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hir>) -> bool { +fn check_is_multi_part<'hir>(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hir>) -> bool { if let ExprKind::Closure(_, _, body_id, _, _) = closure_expr.kind { let mut scanner = IsMultiSpanScanner::new(cx); intravisit::walk_body(&mut scanner, cx.tcx.hir().body(body_id)); @@ -825,7 +824,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(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::Local<'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); @@ -835,7 +834,7 @@ fn get_parent_local(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Opti None } -fn get_parent_local_hir_id(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::Local<'hir>> { let map = cx.tcx.hir(); match map.find(map.get_parent_node(hir_id)) { diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs index b67448e3a57..dc385ebacba 100644 --- a/src/tools/clippy/clippy_lints/src/utils/mod.rs +++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs @@ -1,5 +1,5 @@ pub mod author; pub mod conf; pub mod inspector; -#[cfg(any(feature = "internal-lints", feature = "metadata-collector-lint"))] +#[cfg(feature = "internal")] pub mod internal_lints; diff --git a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs index 1bc0eb6303c..43474da3450 100644 --- a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs +++ b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs @@ -77,7 +77,7 @@ impl VecPushSearcher { } } -impl LateLintPass<'_> for VecInitThenPush { +impl<'tcx> LateLintPass<'tcx> for VecInitThenPush { fn check_block(&mut self, _: &LateContext<'tcx>, _: &'tcx Block<'tcx>) { self.searcher = None; } diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index 0ba0b59ed13..afff6491aba 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -1,17 +1,17 @@ [package] name = "clippy_utils" -version = "0.1.59" +version = "0.1.60" edition = "2021" publish = false [dependencies] +arrayvec = { version = "0.7", default-features = false } if_chain = "1.0" rustc-semver = "1.1" [features] deny-warnings = [] -internal-lints = [] -metadata-collector-lint = [] +internal = [] [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)] diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index 8400bfbbd99..3d3180521ab 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -5,7 +5,6 @@ #![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)] use crate::{both, over}; -use if_chain::if_chain; use rustc_ast::ptr::P; use rustc_ast::{self as ast, *}; use rustc_span::symbol::Ident; @@ -679,34 +678,3 @@ pub fn eq_mac_args(l: &MacArgs, r: &MacArgs) -> bool { _ => false, } } - -/// Extract args from an assert-like macro. -/// -/// Currently working with: -/// - `assert_eq!` and `assert_ne!` -/// - `debug_assert_eq!` and `debug_assert_ne!` -/// -/// For example: -/// -/// `debug_assert_eq!(a, b)` will return Some([a, b]) -pub fn extract_assert_macro_args(mut expr: &Expr) -> Option<[&Expr; 2]> { - if_chain! { - if let ExprKind::If(_, ref block, _) = expr.kind; - if let StmtKind::Semi(ref e) = block.stmts.get(0)?.kind; - then { - expr = e; - } - } - if_chain! { - if let ExprKind::Block(ref block, _) = expr.kind; - if let StmtKind::Expr(ref expr) = block.stmts.get(0)?.kind; - if let ExprKind::Match(ref match_expr, _) = expr.kind; - if let ExprKind::Tup(ref tup) = match_expr.kind; - if let [a, b, ..] = tup.as_slice(); - if let (&ExprKind::AddrOf(_, _, ref a), &ExprKind::AddrOf(_, _, ref b)) = (&a.kind, &b.kind); - then { - return Some([&*a, &*b]); - } - } - None -} diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index 5c024612f8e..34c5af848a6 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -223,7 +223,7 @@ pub fn constant_simple<'tcx>( constant(lcx, typeck_results, e).and_then(|(cst, res)| if res { None } else { Some(cst) }) } -pub fn constant_full_int( +pub fn constant_full_int<'tcx>( lcx: &LateContext<'tcx>, typeck_results: &ty::TypeckResults<'tcx>, e: &Expr<'_>, diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs index d47b002ad7a..ca222c3d669 100644 --- a/src/tools/clippy/clippy_utils/src/diagnostics.rs +++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs @@ -198,7 +198,7 @@ pub fn span_lint_hir_and_then( /// | /// = note: `-D fold-any` implied by `-D warnings` /// ``` -#[cfg_attr(feature = "internal-lints", allow(clippy::collapsible_span_lint_calls))] +#[cfg_attr(feature = "internal", allow(clippy::collapsible_span_lint_calls))] pub fn span_lint_and_sugg<'a, T: LintContext>( cx: &'a T, lint: &'static Lint, diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs index 61e529a6079..c3936ec95d4 100644 --- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs +++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs @@ -45,7 +45,12 @@ impl ops::BitOrAssign for EagernessSuggestion { } /// Determine the eagerness of the given function call. -fn fn_eagerness(cx: &LateContext<'tcx>, fn_id: DefId, name: Symbol, args: &'tcx [Expr<'_>]) -> EagernessSuggestion { +fn fn_eagerness<'tcx>( + cx: &LateContext<'tcx>, + fn_id: DefId, + name: Symbol, + args: &'tcx [Expr<'_>], +) -> EagernessSuggestion { use EagernessSuggestion::{Eager, Lazy, NoChange}; let name = name.as_str(); @@ -92,7 +97,7 @@ fn fn_eagerness(cx: &LateContext<'tcx>, fn_id: DefId, name: Symbol, args: &'tcx } #[allow(clippy::too_many_lines)] -fn expr_eagerness(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion { +fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion { struct V<'cx, 'tcx> { cx: &'cx LateContext<'tcx>, eagerness: EagernessSuggestion, @@ -225,11 +230,11 @@ fn expr_eagerness(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggest } /// Whether the given expression should be changed to evaluate eagerly -pub fn switch_to_eager_eval(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { +pub fn switch_to_eager_eval<'tcx>(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { expr_eagerness(cx, expr) == EagernessSuggestion::Eager } /// Whether the given expression should be changed to evaluate lazily -pub fn switch_to_lazy_eval(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { +pub fn switch_to_lazy_eval<'tcx>(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { expr_eagerness(cx, expr) == EagernessSuggestion::Lazy } diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index c764c35d444..160a51740cd 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -3,15 +3,13 @@ #![deny(clippy::missing_docs_in_private_items)] use crate::ty::is_type_diagnostic_item; -use crate::{is_expn_of, last_path_segment, match_def_path, paths}; +use crate::{is_expn_of, match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast::{self, LitKind}; use rustc_hir as hir; -use rustc_hir::{ - Arm, Block, BorrowKind, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath, StmtKind, UnOp, -}; +use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath}; use rustc_lint::LateContext; -use rustc_span::{sym, symbol, ExpnKind, Span, Symbol}; +use rustc_span::{sym, symbol, Span}; /// The essential nodes of a desugared for loop as well as the entire span: /// `for pat in arg { body }` becomes `(pat, arg, body)`. Return `(pat, arg, body, span)`. @@ -428,293 +426,6 @@ pub fn binop(op: hir::BinOpKind) -> ast::BinOpKind { } } -/// Extract args from an assert-like macro. -/// Currently working with: -/// - `assert!`, `assert_eq!` and `assert_ne!` -/// - `debug_assert!`, `debug_assert_eq!` and `debug_assert_ne!` -/// For example: -/// `assert!(expr)` will return `Some([expr])` -/// `debug_assert_eq!(a, b)` will return `Some([a, b])` -pub fn extract_assert_macro_args<'tcx>(e: &'tcx Expr<'tcx>) -> Option<Vec<&'tcx Expr<'tcx>>> { - /// Try to match the AST for a pattern that contains a match, for example when two args are - /// compared - fn ast_matchblock(matchblock_expr: &'tcx Expr<'tcx>) -> Option<Vec<&Expr<'_>>> { - if_chain! { - if let ExprKind::Match(headerexpr, _, _) = &matchblock_expr.kind; - if let ExprKind::Tup([lhs, rhs]) = &headerexpr.kind; - if let ExprKind::AddrOf(BorrowKind::Ref, _, lhs) = lhs.kind; - if let ExprKind::AddrOf(BorrowKind::Ref, _, rhs) = rhs.kind; - then { - return Some(vec![lhs, rhs]); - } - } - None - } - - if let ExprKind::Block(block, _) = e.kind { - if block.stmts.len() == 1 { - if let StmtKind::Semi(matchexpr) = block.stmts.get(0)?.kind { - // macros with unique arg: `{debug_}assert!` (e.g., `debug_assert!(some_condition)`) - if_chain! { - if let Some(If { cond, .. }) = If::hir(matchexpr); - if let ExprKind::Unary(UnOp::Not, condition) = cond.kind; - then { - return Some(vec![condition]); - } - } - - // debug macros with two args: `debug_assert_{ne, eq}` (e.g., `assert_ne!(a, b)`) - if_chain! { - if let ExprKind::Block(matchblock,_) = matchexpr.kind; - if let Some(matchblock_expr) = matchblock.expr; - then { - return ast_matchblock(matchblock_expr); - } - } - } - } else if let Some(matchblock_expr) = block.expr { - // macros with two args: `assert_{ne, eq}` (e.g., `assert_ne!(a, b)`) - return ast_matchblock(matchblock_expr); - } - } - None -} - -/// A parsed `format!` expansion -pub struct FormatExpn<'tcx> { - /// Span of `format!(..)` - pub call_site: Span, - /// Inner `format_args!` expansion - pub format_args: FormatArgsExpn<'tcx>, -} - -impl FormatExpn<'tcx> { - /// Parses an expanded `format!` invocation - pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> { - if_chain! { - if let ExprKind::Block(block, _) = expr.kind; - if let [stmt] = block.stmts; - if let StmtKind::Local(local) = stmt.kind; - if let Some(init) = local.init; - if let ExprKind::Call(_, [format_args]) = init.kind; - let expn_data = expr.span.ctxt().outer_expn_data(); - if let ExpnKind::Macro(_, sym::format) = expn_data.kind; - if let Some(format_args) = FormatArgsExpn::parse(format_args); - then { - Some(FormatExpn { - call_site: expn_data.call_site, - format_args, - }) - } else { - None - } - } - } -} - -/// A parsed `format_args!` expansion -pub struct FormatArgsExpn<'tcx> { - /// Span of the first argument, the format string - pub format_string_span: Span, - /// Values passed after the format string - pub value_args: Vec<&'tcx Expr<'tcx>>, - - /// String literal expressions which represent the format string split by "{}" - pub format_string_parts: &'tcx [Expr<'tcx>], - /// Symbols corresponding to [`Self::format_string_parts`] - pub format_string_symbols: Vec<Symbol>, - /// Expressions like `ArgumentV1::new(arg0, Debug::fmt)` - pub args: &'tcx [Expr<'tcx>], - /// The final argument passed to `Arguments::new_v1_formatted`, if applicable - pub fmt_expr: Option<&'tcx Expr<'tcx>>, -} - -impl FormatArgsExpn<'tcx> { - /// Parses an expanded `format_args!` or `format_args_nl!` invocation - pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> { - if_chain! { - if let ExpnKind::Macro(_, name) = expr.span.ctxt().outer_expn_data().kind; - let name = name.as_str(); - if name.ends_with("format_args") || name.ends_with("format_args_nl"); - if let ExprKind::Call(_, args) = expr.kind; - if let Some((strs_ref, args, fmt_expr)) = match args { - // Arguments::new_v1 - [strs_ref, args] => Some((strs_ref, args, None)), - // Arguments::new_v1_formatted - [strs_ref, args, fmt_expr, _unsafe_arg] => Some((strs_ref, args, Some(fmt_expr))), - _ => None, - }; - if let ExprKind::AddrOf(BorrowKind::Ref, _, strs_arr) = strs_ref.kind; - if let ExprKind::Array(format_string_parts) = strs_arr.kind; - if let Some(format_string_symbols) = format_string_parts - .iter() - .map(|e| { - if let ExprKind::Lit(lit) = &e.kind { - if let LitKind::Str(symbol, _style) = lit.node { - return Some(symbol); - } - } - None - }) - .collect(); - if let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args.kind; - if let ExprKind::Match(args, [arm], _) = args.kind; - if let ExprKind::Tup(value_args) = args.kind; - if let Some(value_args) = value_args - .iter() - .map(|e| match e.kind { - ExprKind::AddrOf(_, _, e) => Some(e), - _ => None, - }) - .collect(); - if let ExprKind::Array(args) = arm.body.kind; - then { - Some(FormatArgsExpn { - format_string_span: strs_ref.span, - value_args, - format_string_parts, - format_string_symbols, - args, - fmt_expr, - }) - } else { - None - } - } - } - - /// Returns a vector of `FormatArgsArg`. - pub fn args(&self) -> Option<Vec<FormatArgsArg<'tcx>>> { - if let Some(expr) = self.fmt_expr { - if_chain! { - if let ExprKind::AddrOf(BorrowKind::Ref, _, expr) = expr.kind; - if let ExprKind::Array(exprs) = expr.kind; - then { - exprs.iter().map(|fmt| { - if_chain! { - // struct `core::fmt::rt::v1::Argument` - if let ExprKind::Struct(_, fields, _) = fmt.kind; - if let Some(position_field) = fields.iter().find(|f| f.ident.name == sym::position); - if let ExprKind::Lit(lit) = &position_field.expr.kind; - if let LitKind::Int(position, _) = lit.node; - if let Ok(i) = usize::try_from(position); - let arg = &self.args[i]; - if let ExprKind::Call(_, [arg_name, _]) = arg.kind; - if let ExprKind::Field(_, j) = arg_name.kind; - if let Ok(j) = j.name.as_str().parse::<usize>(); - then { - Some(FormatArgsArg { value: self.value_args[j], arg, fmt: Some(fmt) }) - } else { - None - } - } - }).collect() - } else { - None - } - } - } else { - Some( - self.value_args - .iter() - .zip(self.args.iter()) - .map(|(value, arg)| FormatArgsArg { value, arg, fmt: None }) - .collect(), - ) - } - } -} - -/// Type representing a `FormatArgsExpn`'s format arguments -pub struct FormatArgsArg<'tcx> { - /// An element of `value_args` according to `position` - pub value: &'tcx Expr<'tcx>, - /// An element of `args` according to `position` - pub arg: &'tcx Expr<'tcx>, - /// An element of `fmt_expn` - pub fmt: Option<&'tcx Expr<'tcx>>, -} - -impl<'tcx> FormatArgsArg<'tcx> { - /// Returns true if any formatting parameters are used that would have an effect on strings, - /// like `{:+2}` instead of just `{}`. - pub fn has_string_formatting(&self) -> bool { - self.fmt.map_or(false, |fmt| { - // `!` because these conditions check that `self` is unformatted. - !if_chain! { - // struct `core::fmt::rt::v1::Argument` - if let ExprKind::Struct(_, fields, _) = fmt.kind; - if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format); - // struct `core::fmt::rt::v1::FormatSpec` - if let ExprKind::Struct(_, subfields, _) = format_field.expr.kind; - let mut precision_found = false; - let mut width_found = false; - if subfields.iter().all(|field| { - match field.ident.name { - sym::precision => { - precision_found = true; - if let ExprKind::Path(ref precision_path) = field.expr.kind { - last_path_segment(precision_path).ident.name == sym::Implied - } else { - false - } - } - sym::width => { - width_found = true; - if let ExprKind::Path(ref width_qpath) = field.expr.kind { - last_path_segment(width_qpath).ident.name == sym::Implied - } else { - false - } - } - _ => true, - } - }); - if precision_found && width_found; - then { true } else { false } - } - }) - } - - /// Returns true if the argument is formatted using `Display::fmt`. - pub fn is_display(&self) -> bool { - if_chain! { - if let ExprKind::Call(_, [_, format_field]) = self.arg.kind; - if let ExprKind::Path(QPath::Resolved(_, path)) = format_field.kind; - if let [.., t, _] = path.segments; - if t.ident.name == sym::Display; - then { true } else { false } - } - } -} - -/// A parsed `panic!` expansion -pub struct PanicExpn<'tcx> { - /// Span of `panic!(..)` - pub call_site: Span, - /// Inner `format_args!` expansion - pub format_args: FormatArgsExpn<'tcx>, -} - -impl PanicExpn<'tcx> { - /// Parses an expanded `panic!` invocation - pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> { - if_chain! { - if let ExprKind::Call(_, [format_args]) = expr.kind; - let expn_data = expr.span.ctxt().outer_expn_data(); - if let Some(format_args) = FormatArgsExpn::parse(format_args); - then { - Some(PanicExpn { - call_site: expn_data.call_site, - format_args, - }) - } else { - None - } - } - } -} - /// A parsed `Vec` initialization expression #[derive(Clone, Copy)] pub enum VecInitKind { diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index ac2b1a0259e..5a08a411dd1 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -6,9 +6,9 @@ use rustc_data_structures::fx::FxHasher; use rustc_hir::def::Res; use rustc_hir::HirIdMap; use rustc_hir::{ - BinOpKind, Block, BodyId, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, Guard, HirId, + ArrayLen, BinOpKind, Block, BodyId, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, Guard, HirId, InlineAsmOperand, Let, Lifetime, LifetimeName, ParamName, Pat, PatField, PatKind, Path, PathSegment, QPath, Stmt, - StmtKind, Ty, TyKind, TypeBinding, ArrayLen + StmtKind, Ty, TyKind, TypeBinding, }; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::LateContext; @@ -171,11 +171,11 @@ impl HirEqInterExpr<'_, '_, '_> { } pub fn eq_array_length(&mut self, left: ArrayLen, right: ArrayLen) -> bool { - match (left, right) { - (ArrayLen::Infer(..), ArrayLen::Infer(..)) => true, - (ArrayLen::Body(l_ct), ArrayLen::Body(r_ct)) => self.eq_body(l_ct.body, r_ct.body), - (_, _) => false, - } + match (left, right) { + (ArrayLen::Infer(..), ArrayLen::Infer(..)) => true, + (ArrayLen::Body(l_ct), ArrayLen::Body(r_ct)) => self.eq_body(l_ct.body, r_ct.body), + (_, _) => false, + } } pub fn eq_body(&mut self, left: BodyId, right: BodyId) -> bool { @@ -396,12 +396,10 @@ impl HirEqInterExpr<'_, '_, '_> { } #[allow(clippy::similar_names)] - fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool { + pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool { match (&left.kind, &right.kind) { (&TyKind::Slice(l_vec), &TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec), - (&TyKind::Array(lt, ll), &TyKind::Array(rt, rl)) => { - self.eq_ty(lt, rt) && self.eq_array_length(ll, rl) - }, + (&TyKind::Array(lt, ll), &TyKind::Array(rt, rl)) => self.eq_ty(lt, rt) && self.eq_array_length(ll, rl), (&TyKind::Ptr(ref l_mut), &TyKind::Ptr(ref r_mut)) => { l_mut.mutbl == r_mut.mutbl && self.eq_ty(&*l_mut.ty, &*r_mut.ty) }, @@ -853,6 +851,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { pub fn hash_path(&mut self, path: &Path<'_>) { match path.res { // constant hash since equality is dependant on inter-expression context + // e.g. The expressions `if let Some(x) = foo() {}` and `if let Some(y) = foo() {}` are considered equal + // even though the binding names are different and they have different `HirId`s. Res::Local(_) => 1_usize.hash(&mut self.s), _ => { for seg in path.segments { @@ -963,7 +963,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { pub fn hash_array_length(&mut self, length: ArrayLen) { match length { - ArrayLen::Infer(..) => {} + ArrayLen::Infer(..) => {}, ArrayLen::Body(anon_const) => self.hash_body(anon_const.body), } } diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 9179e67c4f4..57183b58b2a 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -1,6 +1,5 @@ #![feature(box_patterns)] #![feature(control_flow_enum)] -#![feature(in_band_lifetimes)] #![feature(let_else)] #![feature(once_cell)] #![feature(rustc_private)] @@ -44,6 +43,7 @@ pub mod diagnostics; pub mod eager_or_lazy; pub mod higher; mod hir_utils; +pub mod macros; pub mod msrvs; pub mod numeric_literal; pub mod paths; @@ -70,19 +70,18 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unhash::UnhashMap; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{CrateNum, DefId}; use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{walk_expr, ErasedMap, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk}; use rustc_hir::{ - def, Arm, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr, ExprKind, FnDecl, - ForeignItem, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, - MatchSource, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, - TraitItemKind, TraitRef, TyKind, UnOp, ArrayLen + def, lang_items, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr, + ExprKind, FnDecl, ForeignItem, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, + Local, MatchSource, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, + Target, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp, }; use rustc_lint::{LateContext, Level, Lint, LintContext}; -use rustc_middle::hir::exports::Export; use rustc_middle::hir::map::Map; use rustc_middle::hir::place::PlaceBase; use rustc_middle::ty as rustc_ty; @@ -127,7 +126,7 @@ macro_rules! extract_msrv_attr { extract_msrv_attr!(@EarlyContext); }; (@$context:ident$(, $call:tt)?) => { - fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'tcx>, attrs: &'tcx [rustc_ast::ast::Attribute]) { + fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) { use $crate::get_unique_inner_attr; match get_unique_inner_attr(cx.sess$($call)?, attrs, "msrv") { Some(msrv_attr) => { @@ -147,13 +146,6 @@ macro_rules! extract_msrv_attr { }; } -/// Returns `true` if the span comes from a macro expansion, no matter if from a -/// macro by example or from a procedural macro -#[must_use] -pub fn in_macro(span: Span) -> bool { - span.from_expansion() && !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..)) -} - /// Returns `true` if the two spans come from differing expansions (i.e., one is /// from a macro and one isn't). #[must_use] @@ -283,7 +275,11 @@ pub fn is_wild(pat: &Pat<'_>) -> bool { } /// Checks if the first type parameter is a lang item. -pub fn is_ty_param_lang_item(cx: &LateContext<'_>, qpath: &QPath<'tcx>, item: LangItem) -> Option<&'tcx hir::Ty<'tcx>> { +pub fn is_ty_param_lang_item<'tcx>( + cx: &LateContext<'_>, + qpath: &QPath<'tcx>, + item: LangItem, +) -> Option<&'tcx hir::Ty<'tcx>> { let ty = get_qpath_generic_tys(qpath).next()?; if let TyKind::Path(qpath) = &ty.kind { @@ -299,7 +295,7 @@ pub fn is_ty_param_lang_item(cx: &LateContext<'_>, qpath: &QPath<'tcx>, item: La } /// Checks if the first type parameter is a diagnostic item. -pub fn is_ty_param_diagnostic_item( +pub fn is_ty_param_diagnostic_item<'tcx>( cx: &LateContext<'_>, qpath: &QPath<'tcx>, item: Symbol, @@ -376,7 +372,7 @@ pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> { } } -pub fn get_qpath_generics(path: &QPath<'tcx>) -> Option<&'tcx GenericArgs<'tcx>> { +pub fn get_qpath_generics<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx GenericArgs<'tcx>> { match path { QPath::Resolved(_, p) => p.segments.last().and_then(|s| s.args), QPath::TypeRelative(_, s) => s.args, @@ -384,7 +380,7 @@ pub fn get_qpath_generics(path: &QPath<'tcx>) -> Option<&'tcx GenericArgs<'tcx>> } } -pub fn get_qpath_generic_tys(path: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> { +pub fn get_qpath_generic_tys<'tcx>(path: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> { get_qpath_generics(path) .map_or([].as_ref(), |a| a.args) .iter() @@ -523,35 +519,59 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Res { } }; } - fn item_child_by_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str) -> Option<&'tcx Export> { - tcx.item_children(def_id) + fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: &str) -> Option<Res> { + match tcx.def_kind(def_id) { + DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx + .module_children(def_id) + .iter() + .find(|item| item.ident.name.as_str() == name) + .map(|child| child.res.expect_non_local()), + DefKind::Impl => tcx + .associated_item_def_ids(def_id) + .iter() + .copied() + .find(|assoc_def_id| tcx.item_name(*assoc_def_id).as_str() == name) + .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id)), + _ => None, + } + } + fn find_primitive(tcx: TyCtxt<'_>, name: &str) -> Option<DefId> { + if let Some(&(index, Target::Impl)) = lang_items::ITEM_REFS.get(&Symbol::intern(name)) { + tcx.lang_items().items()[index] + } else { + None + } + } + fn find_crate(tcx: TyCtxt<'_>, name: &str) -> Option<DefId> { + tcx.crates(()) .iter() - .find(|item| item.ident.name.as_str() == name) + .find(|&&num| tcx.crate_name(num).as_str() == name) + .map(CrateNum::as_def_id) } - let (krate, first, path) = match *path { - [krate, first, ref path @ ..] => (krate, first, path), + let (base, first, path) = match *path { + [base, first, ref path @ ..] => (base, first, path), [primitive] => { return PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy); }, _ => return Res::Err, }; let tcx = cx.tcx; - let crates = tcx.crates(()); - let krate = try_res!(crates.iter().find(|&&num| tcx.crate_name(num).as_str() == krate)); - let first = try_res!(item_child_by_name(tcx, krate.as_def_id(), first)); + let first = try_res!( + find_primitive(tcx, base) + .or_else(|| find_crate(tcx, base)) + .and_then(|id| item_child_by_name(tcx, id, first)) + ); + let last = path .iter() .copied() - // `get_def_path` seems to generate these empty segments for extern blocks. - // We can just ignore them. - .filter(|segment| !segment.is_empty()) // for each segment, find the child item - .try_fold(first, |item, segment| { - let def_id = item.res.def_id(); + .try_fold(first, |res, segment| { + let def_id = res.def_id(); if let Some(item) = item_child_by_name(tcx, def_id, segment) { Some(item) - } else if matches!(item.res, Res::Def(DefKind::Enum | DefKind::Struct, _)) { + } else if matches!(res, Res::Def(DefKind::Enum | DefKind::Struct, _)) { // it is not a child item so check inherent impl items tcx.inherent_impls(def_id) .iter() @@ -560,7 +580,7 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Res { None } }); - try_res!(last).res.expect_non_local() + try_res!(last).expect_non_local() } /// Convenience function to get the `DefId` of a trait by path. @@ -621,6 +641,19 @@ fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &' (result, root) } +/// Gets the mutability of the custom deref adjustment, if any. +pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> { + cx.typeck_results() + .expr_adjustments(e) + .iter() + .find_map(|a| match a.kind { + Adjust::Deref(Some(d)) => Some(Some(d.mutbl)), + Adjust::Deref(None) => None, + _ => Some(None), + }) + .and_then(|x| x) +} + /// Checks if two expressions can be mutably borrowed simultaneously /// and they aren't dependent on borrowing same thing twice pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool { @@ -629,7 +662,15 @@ pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) - if !eq_expr_value(cx, r1, r2) { return true; } + if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() { + return false; + } + for (x1, x2) in s1.iter().zip(s2.iter()) { + if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() { + return false; + } + match (&x1.kind, &x2.kind) { (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => { if i1 != i2 { @@ -703,8 +744,7 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { _ => false, }, ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)), - ExprKind::Repeat(x, len) => if_chain! { - if let ArrayLen::Body(len) = len; + ExprKind::Repeat(x, ArrayLen::Body(len)) => if_chain! { if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind; if let LitKind::Int(v, _) = const_lit.node; if v <= 32 && is_default_equivalent(cx, x); @@ -753,7 +793,7 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { /// /// Note that this check is not recursive, so passing the `if` expression will always return true /// even though sub-expressions might return false. -pub fn can_move_expr_to_closure_no_visit( +pub fn can_move_expr_to_closure_no_visit<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_ids: &[HirId], @@ -828,7 +868,7 @@ impl std::ops::BitOrAssign for CaptureKind { /// Note as this will walk up to parent expressions until the capture can be determined it should /// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or /// function argument (other than a receiver). -pub fn capture_local_usage(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind { +pub fn capture_local_usage<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind { fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind { let mut capture = CaptureKind::Ref(Mutability::Not); pat.each_binding_or_first(&mut |_, id, span, _| match cx @@ -928,7 +968,7 @@ pub fn capture_local_usage(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind /// Checks if the expression can be moved into a closure as is. This will return a list of captures /// if so, otherwise, `None`. -pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> { +pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> { struct V<'cx, 'tcx> { cx: &'cx LateContext<'tcx>, // Stack of potential break targets contained in the expression. @@ -941,7 +981,7 @@ pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> /// mutable reference. captures: HirIdMap<CaptureKind>, } - impl Visitor<'tcx> for V<'_, 'tcx> { + impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> { type Map = ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { NestedVisitorMap::None @@ -969,8 +1009,8 @@ pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> }; if !self.locals.contains(&local_id) { let capture = match capture.info.capture_kind { - UpvarCapture::ByValue(_) => CaptureKind::Value, - UpvarCapture::ByRef(borrow) => match borrow.kind { + UpvarCapture::ByValue => CaptureKind::Value, + UpvarCapture::ByRef(kind) => match kind { BorrowKind::ImmBorrow => CaptureKind::Ref(Mutability::Not), BorrowKind::UniqueImmBorrow | BorrowKind::MutBorrow => { CaptureKind::Ref(Mutability::Mut) @@ -1139,19 +1179,6 @@ pub fn contains_return(expr: &hir::Expr<'_>) -> bool { found } -/// Finds calls of the specified macros in a function body. -pub fn find_macro_calls(names: &[&str], body: &Body<'_>) -> Vec<Span> { - let mut result = Vec::new(); - expr_visitor_no_bodies(|expr| { - if names.iter().any(|fun| is_expn_of(expr.span, fun).is_some()) { - result.push(expr.span); - } - true - }) - .visit_expr(&body.value); - result -} - /// Extends the span to the beginning of the spans line, incl. whitespaces. /// /// ```rust @@ -1211,7 +1238,7 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio } /// Gets the loop or closure enclosing the given expression, if any. -pub fn get_enclosing_loop_or_closure(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> { +pub fn get_enclosing_loop_or_closure<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> { for (_, node) in tcx.hir().parent_iter(expr.hir_id) { match node { Node::Expr( @@ -1680,32 +1707,6 @@ pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool { path.first().map_or(false, |s| s.as_str() == "libc") && path.last().map_or(false, |s| s.as_str() == name) } -pub fn match_panic_call(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { - if let ExprKind::Call(func, [arg]) = expr.kind { - expr_path_res(cx, func) - .opt_def_id() - .map_or(false, |id| match_panic_def_id(cx, id)) - .then(|| arg) - } else { - None - } -} - -pub fn match_panic_def_id(cx: &LateContext<'_>, did: DefId) -> bool { - match_any_def_paths( - cx, - did, - &[ - &paths::BEGIN_PANIC, - &paths::PANIC_ANY, - &paths::PANICKING_PANIC, - &paths::PANICKING_PANIC_FMT, - &paths::PANICKING_PANIC_STR, - ], - ) - .is_some() -} - /// Returns the list of condition expressions and the list of blocks in a /// sequence of `if/else`. /// E.g., this returns `([a, b], [c, d, e])` for the expression @@ -1745,7 +1746,7 @@ pub fn is_async_fn(kind: FnKind<'_>) -> bool { } /// Peels away all the compiler generated code surrounding the body of an async function, -pub fn get_async_fn_body(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> { +pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> { if let ExprKind::Call( _, &[ @@ -1849,7 +1850,7 @@ pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool } /// Gets the node where an expression is either used, or it's type is unified with another branch. -pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> { +pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> { let mut child_id = expr.hir_id; let mut iter = tcx.hir().parent_iter(child_id); loop { @@ -2055,8 +2056,8 @@ where /// Peels off all references on the pattern. Returns the underlying pattern and the number of /// references removed. -pub fn peel_hir_pat_refs(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) { - fn peel(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) { +pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) { + fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) { if let PatKind::Ref(pat, _) = pat.kind { peel(pat, count + 1) } else { @@ -2079,7 +2080,7 @@ pub fn peel_hir_expr_while<'tcx>( /// Peels off up to the given number of references on the expression. Returns the underlying /// expression and the number of references removed. -pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) { +pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) { let mut remaining = count; let e = peel_hir_expr_while(expr, |e| match e.kind { ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => { @@ -2093,7 +2094,7 @@ pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, /// Peels off all references on the expression. Returns the underlying expression and the number of /// references removed. -pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) { +pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) { let mut count = 0; let e = peel_hir_expr_while(expr, |e| match e.kind { ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => { @@ -2176,7 +2177,7 @@ impl<'hir> ItemLikeVisitor<'hir> for TestItemNamesVisitor<'hir> { static TEST_ITEM_NAMES_CACHE: SyncOnceCell<Mutex<FxHashMap<LocalDefId, Vec<Symbol>>>> = SyncOnceCell::new(); -fn with_test_item_names(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool { +fn with_test_item_names<'tcx>(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool { let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default())); let mut map: MutexGuard<'_, FxHashMap<LocalDefId, Vec<Symbol>>> = cache.lock().unwrap(); match map.entry(module) { diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs new file mode 100644 index 00000000000..b7a242cf90a --- /dev/null +++ b/src/tools/clippy/clippy_utils/src/macros.rs @@ -0,0 +1,539 @@ +#![allow(clippy::similar_names)] // `expr` and `expn` + +use crate::visitors::expr_visitor_no_bodies; + +use arrayvec::ArrayVec; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_hir::intravisit::Visitor; +use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath}; +use rustc_lint::LateContext; +use rustc_span::def_id::DefId; +use rustc_span::hygiene::{MacroKind, SyntaxContext}; +use rustc_span::{sym, ExpnData, ExpnId, ExpnKind, Span, Symbol}; +use std::ops::ControlFlow; + +/// A macro call, like `vec![1, 2, 3]`. +/// +/// Use `tcx.item_name(macro_call.def_id)` to get the macro name. +/// Even better is to check if it is a diagnostic item. +/// +/// This structure is similar to `ExpnData` but it precludes desugaring expansions. +#[derive(Debug)] +pub struct MacroCall { + /// Macro `DefId` + pub def_id: DefId, + /// Kind of macro + pub kind: MacroKind, + /// The expansion produced by the macro call + pub expn: ExpnId, + /// Span of the macro call site + pub span: Span, +} + +impl MacroCall { + pub fn is_local(&self) -> bool { + span_is_local(self.span) + } +} + +/// Returns an iterator of expansions that created the given span +pub fn expn_backtrace(mut span: Span) -> impl Iterator<Item = (ExpnId, ExpnData)> { + std::iter::from_fn(move || { + let ctxt = span.ctxt(); + if ctxt == SyntaxContext::root() { + return None; + } + let expn = ctxt.outer_expn(); + let data = expn.expn_data(); + span = data.call_site; + Some((expn, data)) + }) +} + +/// Checks whether the span is from the root expansion or a locally defined macro +pub fn span_is_local(span: Span) -> bool { + !span.from_expansion() || expn_is_local(span.ctxt().outer_expn()) +} + +/// Checks whether the expansion is the root expansion or a locally defined macro +pub fn expn_is_local(expn: ExpnId) -> bool { + if expn == ExpnId::root() { + return true; + } + let data = expn.expn_data(); + let backtrace = expn_backtrace(data.call_site); + std::iter::once((expn, data)) + .chain(backtrace) + .find_map(|(_, data)| data.macro_def_id) + .map_or(true, DefId::is_local) +} + +/// Returns an iterator of macro expansions that created the given span. +/// Note that desugaring expansions are skipped. +pub fn macro_backtrace(span: Span) -> impl Iterator<Item = MacroCall> { + expn_backtrace(span).filter_map(|(expn, data)| match data { + ExpnData { + kind: ExpnKind::Macro(kind, _), + macro_def_id: Some(def_id), + call_site: span, + .. + } => Some(MacroCall { + def_id, + kind, + expn, + span, + }), + _ => None, + }) +} + +/// If the macro backtrace of `span` has a macro call at the root expansion +/// (i.e. not a nested macro call), returns `Some` with the `MacroCall` +pub fn root_macro_call(span: Span) -> Option<MacroCall> { + macro_backtrace(span).last() +} + +/// Like [`root_macro_call`], but only returns `Some` if `node` is the "first node" +/// produced by the macro call, as in [`first_node_in_macro`]. +pub fn root_macro_call_first_node(cx: &LateContext<'_>, node: &impl HirNode) -> Option<MacroCall> { + if first_node_in_macro(cx, node) != Some(ExpnId::root()) { + return None; + } + root_macro_call(node.span()) +} + +/// Like [`macro_backtrace`], but only returns macro calls where `node` is the "first node" of the +/// macro call, as in [`first_node_in_macro`]. +pub fn first_node_macro_backtrace(cx: &LateContext<'_>, node: &impl HirNode) -> impl Iterator<Item = MacroCall> { + let span = node.span(); + first_node_in_macro(cx, node) + .into_iter() + .flat_map(move |expn| macro_backtrace(span).take_while(move |macro_call| macro_call.expn != expn)) +} + +/// If `node` is the "first node" in a macro expansion, returns `Some` with the `ExpnId` of the +/// macro call site (i.e. the parent of the macro expansion). This generally means that `node` +/// is the outermost node of an entire macro expansion, but there are some caveats noted below. +/// This is useful for finding macro calls while visiting the HIR without processing the macro call +/// at every node within its expansion. +/// +/// If you already have immediate access to the parent node, it is simpler to +/// just check the context of that span directly (e.g. `parent.span.from_expansion()`). +/// +/// If a macro call is in statement position, it expands to one or more statements. +/// In that case, each statement *and* their immediate descendants will all yield `Some` +/// with the `ExpnId` of the containing block. +/// +/// A node may be the "first node" of multiple macro calls in a macro backtrace. +/// The expansion of the outermost macro call site is returned in such cases. +pub fn first_node_in_macro(cx: &LateContext<'_>, node: &impl HirNode) -> Option<ExpnId> { + // get the macro expansion or return `None` if not found + // `macro_backtrace` importantly ignores desugaring expansions + let expn = macro_backtrace(node.span()).next()?.expn; + + // get the parent node, possibly skipping over a statement + // if the parent is not found, it is sensible to return `Some(root)` + let hir = cx.tcx.hir(); + let mut parent_iter = hir.parent_iter(node.hir_id()); + let (parent_id, _) = match parent_iter.next() { + None => return Some(ExpnId::root()), + Some((_, Node::Stmt(_))) => match parent_iter.next() { + None => return Some(ExpnId::root()), + Some(next) => next, + }, + Some(next) => next, + }; + + // get the macro expansion of the parent node + let parent_span = hir.span(parent_id); + let Some(parent_macro_call) = macro_backtrace(parent_span).next() else { + // the parent node is not in a macro + return Some(ExpnId::root()); + }; + + if parent_macro_call.expn.is_descendant_of(expn) { + // `node` is input to a macro call + return None; + } + + Some(parent_macro_call.expn) +} + +/* Specific Macro Utils */ + +/// Is `def_id` of `std::panic`, `core::panic` or any inner implementation macros +pub fn is_panic(cx: &LateContext<'_>, def_id: DefId) -> bool { + let Some(name) = cx.tcx.get_diagnostic_name(def_id) else { return false }; + matches!( + name.as_str(), + "core_panic_macro" + | "std_panic_macro" + | "core_panic_2015_macro" + | "std_panic_2015_macro" + | "core_panic_2021_macro" + ) +} + +pub enum PanicExpn<'a> { + /// No arguments - `panic!()` + Empty, + /// A string literal or any `&str` - `panic!("message")` or `panic!(message)` + Str(&'a Expr<'a>), + /// A single argument that implements `Display` - `panic!("{}", object)` + Display(&'a Expr<'a>), + /// Anything else - `panic!("error {}: {}", a, b)` + Format(FormatArgsExpn<'a>), +} + +impl<'a> PanicExpn<'a> { + pub fn parse(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Self> { + if !macro_backtrace(expr.span).any(|macro_call| is_panic(cx, macro_call.def_id)) { + return None; + } + let ExprKind::Call(callee, [arg]) = &expr.kind else { return None }; + let ExprKind::Path(QPath::Resolved(_, path)) = &callee.kind else { return None }; + let result = match path.segments.last().unwrap().ident.as_str() { + "panic" if arg.span.ctxt() == expr.span.ctxt() => Self::Empty, + "panic" | "panic_str" => Self::Str(arg), + "panic_display" => { + let ExprKind::AddrOf(_, _, e) = &arg.kind else { return None }; + Self::Display(e) + }, + "panic_fmt" => Self::Format(FormatArgsExpn::parse(cx, arg)?), + _ => return None, + }; + Some(result) + } +} + +/// Finds the arguments of an `assert!` or `debug_assert!` macro call within the macro expansion +pub fn find_assert_args<'a>( + cx: &LateContext<'_>, + expr: &'a Expr<'a>, + expn: ExpnId, +) -> Option<(&'a Expr<'a>, PanicExpn<'a>)> { + find_assert_args_inner(cx, expr, expn).map(|([e], p)| (e, p)) +} + +/// Finds the arguments of an `assert_eq!` or `debug_assert_eq!` macro call within the macro +/// expansion +pub fn find_assert_eq_args<'a>( + cx: &LateContext<'_>, + expr: &'a Expr<'a>, + expn: ExpnId, +) -> Option<(&'a Expr<'a>, &'a Expr<'a>, PanicExpn<'a>)> { + find_assert_args_inner(cx, expr, expn).map(|([a, b], p)| (a, b, p)) +} + +fn find_assert_args_inner<'a, const N: usize>( + cx: &LateContext<'_>, + expr: &'a Expr<'a>, + expn: ExpnId, +) -> Option<([&'a Expr<'a>; N], PanicExpn<'a>)> { + let macro_id = expn.expn_data().macro_def_id?; + let (expr, expn) = match cx.tcx.item_name(macro_id).as_str().strip_prefix("debug_") { + None => (expr, expn), + Some(inner_name) => find_assert_within_debug_assert(cx, expr, expn, Symbol::intern(inner_name))?, + }; + let mut args = ArrayVec::new(); + let mut panic_expn = None; + expr_visitor_no_bodies(|e| { + if args.is_full() { + if panic_expn.is_none() && e.span.ctxt() != expr.span.ctxt() { + panic_expn = PanicExpn::parse(cx, e); + } + panic_expn.is_none() + } else if is_assert_arg(cx, e, expn) { + args.push(e); + false + } else { + true + } + }) + .visit_expr(expr); + let args = args.into_inner().ok()?; + // if no `panic!(..)` is found, use `PanicExpn::Empty` + // to indicate that the default assertion message is used + let panic_expn = panic_expn.unwrap_or(PanicExpn::Empty); + Some((args, panic_expn)) +} + +fn find_assert_within_debug_assert<'a>( + cx: &LateContext<'_>, + expr: &'a Expr<'a>, + expn: ExpnId, + assert_name: Symbol, +) -> Option<(&'a Expr<'a>, ExpnId)> { + let mut found = None; + expr_visitor_no_bodies(|e| { + if found.is_some() || !e.span.from_expansion() { + return false; + } + let e_expn = e.span.ctxt().outer_expn(); + if e_expn == expn { + return true; + } + if e_expn.expn_data().macro_def_id.map(|id| cx.tcx.item_name(id)) == Some(assert_name) { + found = Some((e, e_expn)); + } + false + }) + .visit_expr(expr); + found +} + +fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) -> bool { + if !expr.span.from_expansion() { + return true; + } + let result = macro_backtrace(expr.span).try_for_each(|macro_call| { + if macro_call.expn == assert_expn { + ControlFlow::Break(false) + } else { + match cx.tcx.item_name(macro_call.def_id) { + // `cfg!(debug_assertions)` in `debug_assert!` + sym::cfg => ControlFlow::CONTINUE, + // assert!(other_macro!(..)) + _ => ControlFlow::Break(true), + } + } + }); + match result { + ControlFlow::Break(is_assert_arg) => is_assert_arg, + ControlFlow::Continue(()) => true, + } +} + +/// A parsed `format_args!` expansion +pub struct FormatArgsExpn<'tcx> { + /// Span of the first argument, the format string + pub format_string_span: Span, + /// The format string split by formatted args like `{..}` + pub format_string_parts: Vec<Symbol>, + /// Values passed after the format string + pub value_args: Vec<&'tcx Expr<'tcx>>, + /// Each element is a `value_args` index and a formatting trait (e.g. `sym::Debug`) + pub formatters: Vec<(usize, Symbol)>, + /// List of `fmt::v1::Argument { .. }` expressions. If this is empty, + /// then `formatters` represents the format args (`{..}`). + /// If this is non-empty, it represents the format args, and the `position` + /// parameters within the struct expressions are indexes of `formatters`. + pub specs: Vec<&'tcx Expr<'tcx>>, +} + +impl<'tcx> FormatArgsExpn<'tcx> { + /// Parses an expanded `format_args!` or `format_args_nl!` invocation + pub fn parse(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<Self> { + macro_backtrace(expr.span).find(|macro_call| { + matches!( + cx.tcx.item_name(macro_call.def_id), + sym::const_format_args | sym::format_args | sym::format_args_nl + ) + })?; + let mut format_string_span: Option<Span> = None; + let mut format_string_parts: Vec<Symbol> = Vec::new(); + let mut value_args: Vec<&Expr<'_>> = Vec::new(); + let mut formatters: Vec<(usize, Symbol)> = Vec::new(); + let mut specs: Vec<&Expr<'_>> = Vec::new(); + expr_visitor_no_bodies(|e| { + // if we're still inside of the macro definition... + if e.span.ctxt() == expr.span.ctxt() { + // ArgumnetV1::new(<value>, <format_trait>::fmt) + if_chain! { + if let ExprKind::Call(callee, [val, fmt_path]) = e.kind; + if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind; + if seg.ident.name == sym::new; + if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind; + if path.segments.last().unwrap().ident.name == sym::ArgumentV1; + if let ExprKind::Path(QPath::Resolved(_, path)) = fmt_path.kind; + if let [.., fmt_trait, _fmt] = path.segments; + then { + let val_idx = if_chain! { + if val.span.ctxt() == expr.span.ctxt(); + if let ExprKind::Field(_, field) = val.kind; + if let Ok(idx) = field.name.as_str().parse(); + then { + // tuple index + idx + } else { + // assume the value expression is passed directly + formatters.len() + } + }; + formatters.push((val_idx, fmt_trait.ident.name)); + } + } + if let ExprKind::Struct(QPath::Resolved(_, path), ..) = e.kind { + if path.segments.last().unwrap().ident.name == sym::Argument { + specs.push(e); + } + } + // walk through the macro expansion + return true; + } + // assume that the first expr with a differing context represents + // (and has the span of) the format string + if format_string_span.is_none() { + format_string_span = Some(e.span); + let span = e.span; + // walk the expr and collect string literals which are format string parts + expr_visitor_no_bodies(|e| { + if e.span.ctxt() != span.ctxt() { + // defensive check, probably doesn't happen + return false; + } + if let ExprKind::Lit(lit) = &e.kind { + if let LitKind::Str(symbol, _s) = lit.node { + format_string_parts.push(symbol); + } + } + true + }) + .visit_expr(e); + } else { + // assume that any further exprs with a differing context are value args + value_args.push(e); + } + // don't walk anything not from the macro expansion (e.a. inputs) + false + }) + .visit_expr(expr); + Some(FormatArgsExpn { + format_string_span: format_string_span?, + format_string_parts, + value_args, + formatters, + specs, + }) + } + + /// Finds a nested call to `format_args!` within a `format!`-like macro call + pub fn find_nested(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expn_id: ExpnId) -> Option<Self> { + let mut format_args = None; + expr_visitor_no_bodies(|e| { + if format_args.is_some() { + return false; + } + let e_ctxt = e.span.ctxt(); + if e_ctxt == expr.span.ctxt() { + return true; + } + if e_ctxt.outer_expn().is_descendant_of(expn_id) { + format_args = FormatArgsExpn::parse(cx, e); + } + false + }) + .visit_expr(expr); + format_args + } + + /// Returns a vector of `FormatArgsArg`. + pub fn args(&self) -> Option<Vec<FormatArgsArg<'tcx>>> { + if self.specs.is_empty() { + let args = std::iter::zip(&self.value_args, &self.formatters) + .map(|(value, &(_, format_trait))| FormatArgsArg { + value, + format_trait, + spec: None, + }) + .collect(); + return Some(args); + } + self.specs + .iter() + .map(|spec| { + if_chain! { + // struct `core::fmt::rt::v1::Argument` + if let ExprKind::Struct(_, fields, _) = spec.kind; + if let Some(position_field) = fields.iter().find(|f| f.ident.name == sym::position); + if let ExprKind::Lit(lit) = &position_field.expr.kind; + if let LitKind::Int(position, _) = lit.node; + if let Ok(i) = usize::try_from(position); + if let Some(&(j, format_trait)) = self.formatters.get(i); + then { + Some(FormatArgsArg { value: self.value_args[j], format_trait, spec: Some(spec) }) + } else { + None + } + } + }) + .collect() + } + + /// Span of all inputs + pub fn inputs_span(&self) -> Span { + match *self.value_args { + [] => self.format_string_span, + [.., last] => self.format_string_span.to(last.span), + } + } +} + +/// Type representing a `FormatArgsExpn`'s format arguments +pub struct FormatArgsArg<'tcx> { + /// An element of `value_args` according to `position` + pub value: &'tcx Expr<'tcx>, + /// An element of `args` according to `position` + pub format_trait: Symbol, + /// An element of `specs` + pub spec: Option<&'tcx Expr<'tcx>>, +} + +impl<'tcx> FormatArgsArg<'tcx> { + /// Returns true if any formatting parameters are used that would have an effect on strings, + /// like `{:+2}` instead of just `{}`. + pub fn has_string_formatting(&self) -> bool { + self.spec.map_or(false, |spec| { + // `!` because these conditions check that `self` is unformatted. + !if_chain! { + // struct `core::fmt::rt::v1::Argument` + if let ExprKind::Struct(_, fields, _) = spec.kind; + if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format); + // struct `core::fmt::rt::v1::FormatSpec` + if let ExprKind::Struct(_, subfields, _) = format_field.expr.kind; + if subfields.iter().all(|field| match field.ident.name { + sym::precision | sym::width => match field.expr.kind { + ExprKind::Path(QPath::Resolved(_, path)) => { + path.segments.last().unwrap().ident.name == sym::Implied + } + _ => false, + } + _ => true, + }); + then { true } else { false } + } + }) + } +} + +/// A node with a `HirId` and a `Span` +pub trait HirNode { + fn hir_id(&self) -> HirId; + fn span(&self) -> Span; +} + +macro_rules! impl_hir_node { + ($($t:ident),*) => { + $(impl HirNode for hir::$t<'_> { + fn hir_id(&self) -> HirId { + self.hir_id + } + fn span(&self) -> Span { + self.span + } + })* + }; +} + +impl_hir_node!(Expr, Pat); + +impl HirNode for hir::Item<'_> { + fn hir_id(&self) -> HirId { + self.hir_id() + } + + fn span(&self) -> Span { + self.span + } +} diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs index 0cec7d6a5e4..a5b409ad96b 100644 --- a/src/tools/clippy/clippy_utils/src/msrvs.rs +++ b/src/tools/clippy/clippy_utils/src/msrvs.rs @@ -12,8 +12,9 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { - 1,53,0 { OR_PATTERNS } + 1,53,0 { OR_PATTERNS, MANUAL_BITS } 1,52,0 { STR_SPLIT_ONCE } + 1,51,0 { BORROW_AS_PTR } 1,50,0 { BOOL_THEN } 1,47,0 { TAU } 1,46,0 { CONST_IF_MATCH } diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index 6171823abbb..288c56e9fd7 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/clippy_utils/src/paths.rs @@ -5,16 +5,16 @@ //! See <https://github.com/rust-lang/rust-clippy/issues/5393> for more information. pub const ANY_TRAIT: [&str; 3] = ["core", "any", "Any"]; -#[cfg(feature = "metadata-collector-lint")] +#[cfg(feature = "internal")] pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"]; -#[cfg(feature = "metadata-collector-lint")] +#[cfg(feature = "internal")] pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [ ["rustc_lint_defs", "Applicability", "Unspecified"], ["rustc_lint_defs", "Applicability", "HasPlaceholders"], ["rustc_lint_defs", "Applicability", "MaybeIncorrect"], ["rustc_lint_defs", "Applicability", "MachineApplicable"], ]; -#[cfg(feature = "metadata-collector-lint")] +#[cfg(feature = "internal")] pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"]; pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"]; #[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros @@ -25,7 +25,6 @@ pub const ASSERT_MACRO: [&str; 4] = ["core", "macros", "builtin", "assert"]; pub const ASSERT_NE_MACRO: [&str; 3] = ["core", "macros", "assert_ne"]; pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"]; pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"]; -pub(super) const BEGIN_PANIC: [&str; 3] = ["std", "panicking", "begin_panic"]; /// Preferably use the diagnostic item `sym::Borrow` where possible pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"]; pub const BORROW_MUT_TRAIT: [&str; 3] = ["core", "borrow", "BorrowMut"]; @@ -46,7 +45,7 @@ pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"]; pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"]; pub const DROP: [&str; 3] = ["core", "mem", "drop"]; pub const DURATION: [&str; 3] = ["core", "time", "Duration"]; -#[cfg(feature = "internal-lints")] +#[cfg(feature = "internal")] pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"]; #[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros pub const EPRINT_MACRO: [&str; 3] = ["std", "macros", "eprint"]; @@ -64,13 +63,17 @@ pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "From pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"]; pub const FROM_STR_METHOD: [&str; 5] = ["core", "str", "traits", "FromStr", "from_str"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; +#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"]; +#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWriteExt"]; pub const HASH: [&str; 3] = ["core", "hash", "Hash"]; pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"]; pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"]; pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"]; -#[cfg(feature = "internal-lints")] +#[cfg(feature = "internal")] pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"]; -#[cfg(feature = "internal-lints")] +#[cfg(feature = "internal")] pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"]; pub const INDEX: [&str; 3] = ["core", "ops", "Index"]; pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"]; @@ -82,11 +85,11 @@ pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"]; pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"]; #[allow(clippy::invalid_paths)] // internal lints do not know about all external crates pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"]; -#[cfg(feature = "internal-lints")] +#[cfg(feature = "internal")] pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; -#[cfg(feature = "internal-lints")] +#[cfg(feature = "internal")] pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; -#[cfg(any(feature = "internal-lints", feature = "metadata-collector-lint"))] +#[cfg(feature = "internal")] pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"]; pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"]; pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"]; @@ -106,10 +109,6 @@ pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"]; pub const ORD: [&str; 3] = ["core", "cmp", "Ord"]; pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"]; pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"]; -pub(super) const PANICKING_PANIC: [&str; 3] = ["core", "panicking", "panic"]; -pub(super) const PANICKING_PANIC_FMT: [&str; 3] = ["core", "panicking", "panic_fmt"]; -pub(super) const PANICKING_PANIC_STR: [&str; 3] = ["core", "panicking", "panic_str"]; -pub(super) const PANIC_ANY: [&str; 3] = ["std", "panic", "panic_any"]; pub const PARKING_LOT_RAWMUTEX: [&str; 3] = ["parking_lot", "raw_mutex", "RawMutex"]; pub const PARKING_LOT_RAWRWLOCK: [&str; 3] = ["parking_lot", "raw_rwlock", "RawRwLock"]; pub const PARKING_LOT_MUTEX_GUARD: [&str; 2] = ["parking_lot", "MutexGuard"]; @@ -118,6 +117,7 @@ pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 2] = ["parking_lot", "RwLockWri pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"]; pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"]; pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"]; +#[cfg_attr(not(unix), allow(clippy::invalid_paths))] pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "PermissionsExt", "from_mode"]; pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"]; pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"]; @@ -180,20 +180,24 @@ pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"]; pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"]; pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"]; pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"]; -#[cfg(feature = "internal-lints")] +#[cfg(feature = "internal")] pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"]; -#[cfg(feature = "internal-lints")] +#[cfg(feature = "internal")] pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"]; -#[cfg(feature = "internal-lints")] +#[cfg(feature = "internal")] pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"]; -#[cfg(feature = "internal-lints")] +#[cfg(feature = "internal")] pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"]; -#[cfg(feature = "internal-lints")] +#[cfg(feature = "internal")] pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"]; -#[cfg(feature = "internal-lints")] +#[cfg(feature = "internal")] pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"]; pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"]; +#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +pub const TOKIO_IO_ASYNCREADEXT: [&str; 5] = ["tokio", "io", "util", "async_read_ext", "AsyncReadExt"]; +#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +pub const TOKIO_IO_ASYNCWRITEEXT: [&str; 5] = ["tokio", "io", "util", "async_write_ext", "AsyncWriteExt"]; pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"]; pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"]; pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"]; 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 836558b07cb..8bdc9a9ea16 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 @@ -19,7 +19,7 @@ use std::borrow::Cow; type McfResult = Result<(), (Span, Cow<'static, str>)>; -pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&RustcVersion>) -> McfResult { +pub fn is_min_const_fn<'a, 'tcx>(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&RustcVersion>) -> McfResult { let def_id = body.source.def_id(); let mut current = def_id; loop { @@ -85,7 +85,7 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&Ru Ok(()) } -fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult { +fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult { for arg in ty.walk(tcx) { let ty = match arg.unpack() { GenericArgKind::Type(ty) => ty, @@ -133,7 +133,13 @@ fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult { Ok(()) } -fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rvalue<'tcx>, span: Span) -> McfResult { +fn check_rvalue<'tcx>( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + def_id: DefId, + rvalue: &Rvalue<'tcx>, + span: Span, +) -> McfResult { match rvalue { Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())), Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => check_operand(tcx, operand, span, body), @@ -210,7 +216,12 @@ fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rv } } -fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statement: &Statement<'tcx>) -> McfResult { +fn check_statement<'tcx>( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + def_id: DefId, + statement: &Statement<'tcx>, +) -> McfResult { let span = statement.source_info.span; match &statement.kind { StatementKind::Assign(box (place, rval)) => { @@ -239,7 +250,7 @@ fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statemen } } -fn check_operand(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult { +fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult { match operand { Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body), Operand::Constant(c) => match c.check_static_ptr(tcx) { @@ -249,7 +260,7 @@ fn check_operand(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: & } } -fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult { +fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult { let mut cursor = place.projection.as_ref(); while let [ref proj_base @ .., elem] = *cursor { cursor = proj_base; @@ -274,7 +285,7 @@ fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'t Ok(()) } -fn check_terminator( +fn check_terminator<'a, 'tcx>( tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Terminator<'tcx>, diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs index d928317259d..dbad607c58e 100644 --- a/src/tools/clippy/clippy_utils/src/source.rs +++ b/src/tools/clippy/clippy_utils/src/source.rs @@ -281,7 +281,7 @@ pub fn snippet_block_with_applicability<'a, T: LintContext>( /// correctly get a snippet of `vec![]`. /// /// This will also return whether or not the snippet is a macro call. -pub fn snippet_with_context( +pub fn snippet_with_context<'a>( cx: &LateContext<'_>, span: Span, outer: SyntaxContext, diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index 92662c59226..87bc8232dde 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -461,7 +461,7 @@ impl Neg for Sugg<'_> { } } -impl Not for Sugg<'a> { +impl<'a> Not for Sugg<'a> { type Output = Sugg<'a>; fn not(self) -> Sugg<'a> { use AssocOp::{Equal, Greater, GreaterEqual, Less, LessEqual, NotEqual}; @@ -846,7 +846,7 @@ struct DerefDelegate<'a, 'tcx> { applicability: Applicability, } -impl DerefDelegate<'_, 'tcx> { +impl<'tcx> DerefDelegate<'_, 'tcx> { /// build final suggestion: /// - create the ending part of suggestion /// - concatenate starting and ending parts diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index 6d191d4a59b..72317447159 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -25,7 +25,7 @@ pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { } /// Checks whether a type can be partially moved. -pub fn can_partially_move_ty(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { +pub fn can_partially_move_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { if has_drop(cx, ty) || is_copy(cx, ty) { return false; } @@ -366,7 +366,7 @@ pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) { /// Returns `true` if types `a` and `b` are same types having same `Const` generic args, /// otherwise returns `false` -pub fn same_type_and_consts(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { +pub fn same_type_and_consts<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { match (&a.kind(), &b.kind()) { (&ty::Adt(did_a, substs_a), &ty::Adt(did_b, substs_b)) => { if did_a != did_b { diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index 4bfd3c64b9c..b60cd4736f3 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -173,7 +173,7 @@ pub trait Visitable<'tcx> { } macro_rules! visitable_ref { ($t:ident, $f:ident) => { - impl Visitable<'tcx> for &'tcx $t<'tcx> { + impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> { fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) { visitor.$f(self); } @@ -217,7 +217,7 @@ pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { } /// Checks if the given local is used. -pub fn is_local_used(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool { +pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool { let mut is_used = false; let mut visitor = expr_visitor(cx, |expr| { if !is_used { @@ -231,7 +231,7 @@ pub fn is_local_used(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id } /// Checks if the given expression is a constant. -pub fn is_const_evaluatable(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { +pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { struct V<'a, 'tcx> { cx: &'a LateContext<'tcx>, is_const: bool, @@ -321,7 +321,7 @@ pub fn is_const_evaluatable(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { } /// Checks if the given expression performs an unsafe operation outside of an unsafe block. -pub fn is_expr_unsafe(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { +pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { struct V<'a, 'tcx> { cx: &'a LateContext<'tcx>, is_unsafe: bool, diff --git a/src/tools/clippy/doc/common_tools_writing_lints.md b/src/tools/clippy/doc/common_tools_writing_lints.md index c7e51d53f51..207b0be1548 100644 --- a/src/tools/clippy/doc/common_tools_writing_lints.md +++ b/src/tools/clippy/doc/common_tools_writing_lints.md @@ -60,7 +60,7 @@ Two noticeable items here: Starting with an `expr`, you can check whether it is calling a specific method `some_method`: ```rust -impl LateLintPass<'_> for MyStructLint { +impl<'tcx> LateLintPass<'tcx> for MyStructLint { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if_chain! { // Check our expr is calling a method diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs index 53e669254cf..9af8dcc7726 100644 --- a/src/tools/clippy/lintcheck/src/main.rs +++ b/src/tools/clippy/lintcheck/src/main.rs @@ -665,16 +665,6 @@ fn lintcheck_needs_rerun(lintcheck_logs_path: &Path) -> bool { logs_modified < clippy_modified } -fn is_in_clippy_root() -> bool { - if let Ok(pb) = std::env::current_dir() { - if let Some(file) = pb.file_name() { - return file == PathBuf::from("rust-clippy"); - } - } - - false -} - /// lintchecks `main()` function /// /// # Panics @@ -683,7 +673,7 @@ fn is_in_clippy_root() -> bool { /// or if lintcheck is executed from the wrong directory (aka none-repo-root) pub fn main() { // assert that we launch lintcheck from the repo root (via cargo lintcheck) - if !is_in_clippy_root() { + if std::fs::metadata("lintcheck/Cargo.toml").is_err() { eprintln!("lintcheck needs to be run from clippys repo root!\nUse `cargo lintcheck` alternatively."); std::process::exit(3); } diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index 471ae40f1ac..e6a58e92072 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-12-30" +channel = "nightly-2022-01-13" components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/tools/clippy/src/main.rs b/src/tools/clippy/src/main.rs index 7ebdd947893..240e233420f 100644 --- a/src/tools/clippy/src/main.rs +++ b/src/tools/clippy/src/main.rs @@ -96,7 +96,7 @@ impl ClippyCmd { clippy_args.push("--no-deps".into()); } - ClippyCmd { + Self { cargo_subcommand, args, clippy_args, diff --git a/src/tools/clippy/tests/cargo/mod.rs b/src/tools/clippy/tests/cargo/mod.rs deleted file mode 100644 index 4dbe71e4b6a..00000000000 --- a/src/tools/clippy/tests/cargo/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[must_use] -pub fn is_rustc_test_suite() -> bool { - option_env!("RUSTC_TEST_SUITE").is_some() -} diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index a2d58491872..531890c863f 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -1,4 +1,5 @@ #![feature(test)] // compiletest_rs requires this attribute +#![feature(once_cell)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] @@ -11,16 +12,18 @@ use std::ffi::{OsStr, OsString}; use std::fs; use std::io; use std::path::{Path, PathBuf}; +use test_utils::IS_RUSTC_TEST_SUITE; -mod cargo; +mod test_utils; // whether to run internal tests or not -const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal-lints"); +const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal"); /// All crates used in UI tests are listed here static TEST_DEPENDENCIES: &[&str] = &[ "clippy_utils", "derive_new", + "futures", "if_chain", "itertools", "quote", @@ -28,6 +31,7 @@ static TEST_DEPENDENCIES: &[&str] = &[ "serde", "serde_derive", "syn", + "tokio", "parking_lot", ]; @@ -38,6 +42,8 @@ extern crate clippy_utils; #[allow(unused_extern_crates)] extern crate derive_new; #[allow(unused_extern_crates)] +extern crate futures; +#[allow(unused_extern_crates)] extern crate if_chain; #[allow(unused_extern_crates)] extern crate itertools; @@ -47,6 +53,8 @@ extern crate parking_lot; extern crate quote; #[allow(unused_extern_crates)] extern crate syn; +#[allow(unused_extern_crates)] +extern crate tokio; /// Produces a string with an `--extern` flag for all UI test crate /// dependencies. @@ -298,7 +306,7 @@ fn run_ui_cargo(config: &mut compiletest::Config) { Ok(result) } - if cargo::is_rustc_test_suite() { + if IS_RUSTC_TEST_SUITE { return; } diff --git a/src/tools/clippy/tests/dogfood.rs b/src/tools/clippy/tests/dogfood.rs index a37cdfed126..67af9d05bf4 100644 --- a/src/tools/clippy/tests/dogfood.rs +++ b/src/tools/clippy/tests/dogfood.rs @@ -3,184 +3,31 @@ //! //! See [Eating your own dog food](https://en.wikipedia.org/wiki/Eating_your_own_dog_food) for context -// Dogfood cannot run on Windows -#![cfg(not(windows))] #![feature(once_cell)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] -use std::lazy::SyncLazy; use std::path::PathBuf; use std::process::Command; +use test_utils::IS_RUSTC_TEST_SUITE; -mod cargo; - -static CLIPPY_PATH: SyncLazy<PathBuf> = SyncLazy::new(|| { - let mut path = std::env::current_exe().unwrap(); - assert!(path.pop()); // deps - path.set_file_name("cargo-clippy"); - path -}); +mod test_utils; #[test] fn dogfood_clippy() { - // run clippy on itself and fail the test if lint warnings are reported - if cargo::is_rustc_test_suite() { - return; - } - let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - - let mut command = Command::new(&*CLIPPY_PATH); - command - .current_dir(root_dir) - .env("CARGO_INCREMENTAL", "0") - .arg("clippy") - .arg("--all-targets") - .arg("--all-features") - .arg("--") - .args(&["-D", "clippy::all"]) - .args(&["-D", "clippy::pedantic"]) - .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir - - // internal lints only exist if we build with the internal-lints feature - if cfg!(feature = "internal-lints") { - command.args(&["-D", "clippy::internal"]); - } - - let output = command.output().unwrap(); - - println!("status: {}", output.status); - println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); - println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); - - assert!(output.status.success()); -} - -fn test_no_deps_ignores_path_deps_in_workspaces() { - if cargo::is_rustc_test_suite() { + if IS_RUSTC_TEST_SUITE { return; } - let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let target_dir = root.join("target").join("dogfood"); - let cwd = root.join("clippy_workspace_tests"); - - // Make sure we start with a clean state - Command::new("cargo") - .current_dir(&cwd) - .env("CARGO_TARGET_DIR", &target_dir) - .arg("clean") - .args(&["-p", "subcrate"]) - .args(&["-p", "path_dep"]) - .output() - .unwrap(); - - // `path_dep` is a path dependency of `subcrate` that would trigger a denied lint. - // Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`. - let output = Command::new(&*CLIPPY_PATH) - .current_dir(&cwd) - .env("CARGO_INCREMENTAL", "0") - .arg("clippy") - .args(&["-p", "subcrate"]) - .arg("--no-deps") - .arg("--") - .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir - .args(&["--cfg", r#"feature="primary_package_test""#]) - .output() - .unwrap(); - println!("status: {}", output.status); - println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); - println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); - - assert!(output.status.success()); - - let lint_path_dep = || { - // Test that without the `--no-deps` argument, `path_dep` is linted. - let output = Command::new(&*CLIPPY_PATH) - .current_dir(&cwd) - .env("CARGO_INCREMENTAL", "0") - .arg("clippy") - .args(&["-p", "subcrate"]) - .arg("--") - .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir - .args(&["--cfg", r#"feature="primary_package_test""#]) - .output() - .unwrap(); - println!("status: {}", output.status); - println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); - println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); - - assert!(!output.status.success()); - assert!( - String::from_utf8(output.stderr) - .unwrap() - .contains("error: empty `loop {}` wastes CPU cycles") - ); - }; - - // Make sure Cargo is aware of the removal of `--no-deps`. - lint_path_dep(); - - let successful_build = || { - let output = Command::new(&*CLIPPY_PATH) - .current_dir(&cwd) - .env("CARGO_INCREMENTAL", "0") - .arg("clippy") - .args(&["-p", "subcrate"]) - .arg("--") - .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir - .output() - .unwrap(); - println!("status: {}", output.status); - println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); - println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); - - assert!(output.status.success()); - - output - }; - - // Trigger a sucessful build, so Cargo would like to cache the build result. - successful_build(); - - // Make sure there's no spurious rebuild when nothing changes. - let stderr = String::from_utf8(successful_build().stderr).unwrap(); - assert!(!stderr.contains("Compiling")); - assert!(!stderr.contains("Checking")); - assert!(stderr.contains("Finished")); - - // Make sure Cargo is aware of the new `--cfg` flag. - lint_path_dep(); -} -#[test] -fn dogfood_subprojects() { - // run clippy on remaining subprojects and fail the test if lint warnings are reported - if cargo::is_rustc_test_suite() { - return; + // "" is the root package + for package in &["", "clippy_dev", "clippy_lints", "clippy_utils", "rustc_tools_util"] { + run_clippy_for_package(package); } - - // NOTE: `path_dep` crate is omitted on purpose here - for project in &[ - "clippy_workspace_tests", - "clippy_workspace_tests/src", - "clippy_workspace_tests/subcrate", - "clippy_workspace_tests/subcrate/src", - "clippy_dev", - "clippy_lints", - "clippy_utils", - "rustc_tools_util", - ] { - run_clippy_for_project(project); - } - - // NOTE: Since tests run in parallel we can't run cargo commands on the same workspace at the - // same time, so we test this immediately after the dogfood for workspaces. - test_no_deps_ignores_path_deps_in_workspaces(); } #[test] #[ignore] -#[cfg(feature = "metadata-collector-lint")] +#[cfg(feature = "internal")] fn run_metadata_collection_lint() { use std::fs::File; use std::time::SystemTime; @@ -191,7 +38,7 @@ fn run_metadata_collection_lint() { // Run collection as is std::env::set_var("ENABLE_METADATA_COLLECTION", "1"); - run_clippy_for_project("clippy_lints"); + run_clippy_for_package("clippy_lints"); // Check if cargo caching got in the way if let Ok(file) = File::open(metadata_output_path) { @@ -214,13 +61,13 @@ fn run_metadata_collection_lint() { .unwrap(); // Running the collection again - run_clippy_for_project("clippy_lints"); + run_clippy_for_package("clippy_lints"); } -fn run_clippy_for_project(project: &str) { +fn run_clippy_for_package(project: &str) { let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let mut command = Command::new(&*CLIPPY_PATH); + let mut command = Command::new(&*test_utils::CARGO_CLIPPY_PATH); command .current_dir(root_dir.join(project)) @@ -233,8 +80,8 @@ fn run_clippy_for_project(project: &str) { .args(&["-D", "clippy::pedantic"]) .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir - // internal lints only exist if we build with the internal-lints feature - if cfg!(feature = "internal-lints") { + // internal lints only exist if we build with the internal feature + if cfg!(feature = "internal") { command.args(&["-D", "clippy::internal"]); } diff --git a/src/tools/clippy/tests/fmt.rs b/src/tools/clippy/tests/fmt.rs index 383702dd439..0defd45b68b 100644 --- a/src/tools/clippy/tests/fmt.rs +++ b/src/tools/clippy/tests/fmt.rs @@ -10,14 +10,6 @@ fn fmt() { return; } - // Skip this test if nightly rustfmt is unavailable - let rustup_output = Command::new("rustup").args(&["component", "list"]).output().unwrap(); - assert!(rustup_output.status.success()); - let component_output = String::from_utf8_lossy(&rustup_output.stdout); - if !component_output.contains("rustfmt") { - return; - } - let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let output = Command::new("cargo") .current_dir(root_dir) diff --git a/src/tools/clippy/tests/test_utils/mod.rs b/src/tools/clippy/tests/test_utils/mod.rs new file mode 100644 index 00000000000..8a4de3f6def --- /dev/null +++ b/src/tools/clippy/tests/test_utils/mod.rs @@ -0,0 +1,13 @@ +#![allow(dead_code)] // see https://github.com/rust-lang/rust/issues/46379 + +use std::lazy::SyncLazy; +use std::path::PathBuf; + +pub static CARGO_CLIPPY_PATH: SyncLazy<PathBuf> = SyncLazy::new(|| { + let mut path = std::env::current_exe().unwrap(); + assert!(path.pop()); // deps + path.set_file_name("cargo-clippy"); + path +}); + +pub const IS_RUSTC_TEST_SUITE: bool = option_env!("RUSTC_TEST_SUITE").is_some(); diff --git a/src/tools/clippy/tests/ui-internal/invalid_paths.stderr b/src/tools/clippy/tests/ui-internal/invalid_paths.stderr index 0a8e5427978..0e850886917 100644 --- a/src/tools/clippy/tests/ui-internal/invalid_paths.stderr +++ b/src/tools/clippy/tests/ui-internal/invalid_paths.stderr @@ -1,10 +1,16 @@ error: invalid path + --> $DIR/invalid_paths.rs:15:5 + | +LL | pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::invalid-paths` implied by `-D warnings` + +error: invalid path --> $DIR/invalid_paths.rs:18:5 | LL | pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::invalid-paths` implied by `-D warnings` error: invalid path --> $DIR/invalid_paths.rs:21:5 @@ -12,5 +18,5 @@ error: invalid path LL | pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/clippy.toml index f1d4a4619c5..c902d21123d 100644 --- a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/clippy.toml +++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/clippy.toml @@ -1,6 +1,8 @@ disallowed-methods = [ # just a string is shorthand for path only "std::iter::Iterator::sum", + "f32::clamp", + "slice::sort_unstable", # can give path and reason with an inline table { path = "regex::Regex::is_match", reason = "no matching allowed" }, # can use an inline table but omit reason diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs index cb449b45bde..338b3b5b28f 100644 --- a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs +++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs @@ -7,6 +7,11 @@ fn main() { let re = Regex::new(r"ab.*c").unwrap(); re.is_match("abc"); - let a = vec![1, 2, 3, 4]; + let mut a = vec![1, 2, 3, 4]; a.iter().sum::<i32>(); + + a.sort_unstable(); + + let _ = 2.0f32.clamp(3.0f32, 4.0f32); + let _ = 2.0f64.clamp(3.0f64, 4.0f64); } diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr index 999ead10d51..5533676aea2 100644 --- a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr @@ -20,5 +20,17 @@ error: use of a disallowed method `std::iter::Iterator::sum` LL | a.iter().sum::<i32>(); | ^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: use of a disallowed method `slice::sort_unstable` + --> $DIR/conf_disallowed_methods.rs:13:5 + | +LL | a.sort_unstable(); + | ^^^^^^^^^^^^^^^^^ + +error: use of a disallowed method `f32::clamp` + --> $DIR/conf_disallowed_methods.rs:15:13 + | +LL | let _ = 2.0f32.clamp(3.0f32, 4.0f32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/as_conversions.rs b/src/tools/clippy/tests/ui/as_conversions.rs index cd745feec6d..ba4394defbf 100644 --- a/src/tools/clippy/tests/ui/as_conversions.rs +++ b/src/tools/clippy/tests/ui/as_conversions.rs @@ -1,6 +1,7 @@ // aux-build:macro_rules.rs #![warn(clippy::as_conversions)] +#![allow(clippy::borrow_as_ptr)] #[macro_use] extern crate macro_rules; diff --git a/src/tools/clippy/tests/ui/as_conversions.stderr b/src/tools/clippy/tests/ui/as_conversions.stderr index f5f75d3aee0..d11b56171b0 100644 --- a/src/tools/clippy/tests/ui/as_conversions.stderr +++ b/src/tools/clippy/tests/ui/as_conversions.stderr @@ -1,5 +1,5 @@ error: using a potentially dangerous silent `as` conversion - --> $DIR/as_conversions.rs:14:13 + --> $DIR/as_conversions.rs:15:13 | LL | let i = 0u32 as u64; | ^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let i = 0u32 as u64; = help: consider using a safe wrapper for this conversion error: using a potentially dangerous silent `as` conversion - --> $DIR/as_conversions.rs:16:13 + --> $DIR/as_conversions.rs:17:13 | LL | let j = &i as *const u64 as *mut u64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | let j = &i as *const u64 as *mut u64; = help: consider using a safe wrapper for this conversion error: using a potentially dangerous silent `as` conversion - --> $DIR/as_conversions.rs:16:13 + --> $DIR/as_conversions.rs:17:13 | LL | let j = &i as *const u64 as *mut u64; | ^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/assertions_on_constants.rs b/src/tools/clippy/tests/ui/assertions_on_constants.rs index cb516d0f977..7477c01ca78 100644 --- a/src/tools/clippy/tests/ui/assertions_on_constants.rs +++ b/src/tools/clippy/tests/ui/assertions_on_constants.rs @@ -1,4 +1,3 @@ -//FIXME: suggestions are wrongly expanded, this should be fixed along with #7843 #![allow(non_fmt_panics)] macro_rules! assert_const { diff --git a/src/tools/clippy/tests/ui/assertions_on_constants.stderr b/src/tools/clippy/tests/ui/assertions_on_constants.stderr index ec80ec702fb..e1f818814d5 100644 --- a/src/tools/clippy/tests/ui/assertions_on_constants.stderr +++ b/src/tools/clippy/tests/ui/assertions_on_constants.stderr @@ -1,75 +1,75 @@ error: `assert!(true)` will be optimized out by the compiler - --> $DIR/assertions_on_constants.rs:11:5 + --> $DIR/assertions_on_constants.rs:10:5 | LL | assert!(true); | ^^^^^^^^^^^^^ | = note: `-D clippy::assertions-on-constants` implied by `-D warnings` = help: remove it - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) error: `assert!(false)` should probably be replaced - --> $DIR/assertions_on_constants.rs:12:5 + --> $DIR/assertions_on_constants.rs:11:5 | LL | assert!(false); | ^^^^^^^^^^^^^^ | = help: use `panic!()` or `unreachable!()` - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) error: `assert!(true)` will be optimized out by the compiler - --> $DIR/assertions_on_constants.rs:13:5 + --> $DIR/assertions_on_constants.rs:12:5 | LL | assert!(true, "true message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: remove it - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) -error: `assert!(false, $crate::const_format_args!($($t)+))` should probably be replaced - --> $DIR/assertions_on_constants.rs:14:5 +error: `assert!(false, ..)` should probably be replaced + --> $DIR/assertions_on_constants.rs:13:5 | LL | assert!(false, "false message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use `panic!($crate::const_format_args!($($t)+))` or `unreachable!($crate::const_format_args!($($t)+))` - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) + = help: use `panic!(..)` or `unreachable!(..)` + +error: `assert!(false, ..)` should probably be replaced + --> $DIR/assertions_on_constants.rs:16:5 + | +LL | assert!(false, "{}", msg.to_uppercase()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `panic!(..)` or `unreachable!(..)` error: `assert!(true)` will be optimized out by the compiler - --> $DIR/assertions_on_constants.rs:20:5 + --> $DIR/assertions_on_constants.rs:19:5 | LL | assert!(B); | ^^^^^^^^^^ | = help: remove it - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) error: `assert!(false)` should probably be replaced - --> $DIR/assertions_on_constants.rs:23:5 + --> $DIR/assertions_on_constants.rs:22:5 | LL | assert!(C); | ^^^^^^^^^^ | = help: use `panic!()` or `unreachable!()` - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) -error: `assert!(false, $crate::const_format_args!($($t)+))` should probably be replaced - --> $DIR/assertions_on_constants.rs:24:5 +error: `assert!(false, ..)` should probably be replaced + --> $DIR/assertions_on_constants.rs:23:5 | LL | assert!(C, "C message"); | ^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use `panic!($crate::const_format_args!($($t)+))` or `unreachable!($crate::const_format_args!($($t)+))` - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) + = help: use `panic!(..)` or `unreachable!(..)` error: `debug_assert!(true)` will be optimized out by the compiler - --> $DIR/assertions_on_constants.rs:26:5 + --> $DIR/assertions_on_constants.rs:25:5 | LL | debug_assert!(true); | ^^^^^^^^^^^^^^^^^^^ | = help: remove it - = note: this error originates in the macro `$crate::assert` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.fixed b/src/tools/clippy/tests/ui/borrow_as_ptr.fixed new file mode 100644 index 00000000000..ff5c6a8c377 --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_as_ptr.fixed @@ -0,0 +1,10 @@ +// run-rustfix +#![warn(clippy::borrow_as_ptr)] + +fn main() { + let val = 1; + let _p = std::ptr::addr_of!(val); + + let mut val_mut = 1; + let _p_mut = std::ptr::addr_of_mut!(val_mut); +} diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.rs b/src/tools/clippy/tests/ui/borrow_as_ptr.rs new file mode 100644 index 00000000000..0f62ec6ee58 --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_as_ptr.rs @@ -0,0 +1,10 @@ +// run-rustfix +#![warn(clippy::borrow_as_ptr)] + +fn main() { + let val = 1; + let _p = &val as *const i32; + + let mut val_mut = 1; + let _p_mut = &mut val_mut as *mut i32; +} diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.stderr b/src/tools/clippy/tests/ui/borrow_as_ptr.stderr new file mode 100644 index 00000000000..be1ed733056 --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_as_ptr.stderr @@ -0,0 +1,16 @@ +error: borrow as raw pointer + --> $DIR/borrow_as_ptr.rs:6:14 + | +LL | let _p = &val as *const i32; + | ^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::addr_of!(val)` + | + = note: `-D clippy::borrow-as-ptr` implied by `-D warnings` + +error: borrow as raw pointer + --> $DIR/borrow_as_ptr.rs:9:18 + | +LL | let _p_mut = &mut val_mut as *mut i32; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::addr_of_mut!(val_mut)` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.fixed b/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.fixed new file mode 100644 index 00000000000..eaba3b1c20c --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.fixed @@ -0,0 +1,22 @@ +// run-rustfix +#![warn(clippy::borrow_as_ptr)] +#![feature(lang_items, start, libc)] +#![no_std] + +#[start] +fn main(_argc: isize, _argv: *const *const u8) -> isize { + let val = 1; + let _p = core::ptr::addr_of!(val); + + let mut val_mut = 1; + let _p_mut = core::ptr::addr_of_mut!(val_mut); + 0 +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.rs b/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.rs new file mode 100644 index 00000000000..d83f9d1f875 --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.rs @@ -0,0 +1,22 @@ +// run-rustfix +#![warn(clippy::borrow_as_ptr)] +#![feature(lang_items, start, libc)] +#![no_std] + +#[start] +fn main(_argc: isize, _argv: *const *const u8) -> isize { + let val = 1; + let _p = &val as *const i32; + + let mut val_mut = 1; + let _p_mut = &mut val_mut as *mut i32; + 0 +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.stderr b/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.stderr new file mode 100644 index 00000000000..84c8ba7d07f --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.stderr @@ -0,0 +1,16 @@ +error: borrow as raw pointer + --> $DIR/borrow_as_ptr_no_std.rs:9:14 + | +LL | let _p = &val as *const i32; + | ^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::addr_of!(val)` + | + = note: `-D clippy::borrow-as-ptr` implied by `-D warnings` + +error: borrow as raw pointer + --> $DIR/borrow_as_ptr_no_std.rs:12:18 + | +LL | let _p_mut = &mut val_mut as *mut i32; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::addr_of_mut!(val_mut)` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/cast_alignment.rs b/src/tools/clippy/tests/ui/cast_alignment.rs index d011e84b115..659591fffbe 100644 --- a/src/tools/clippy/tests/ui/cast_alignment.rs +++ b/src/tools/clippy/tests/ui/cast_alignment.rs @@ -4,7 +4,13 @@ extern crate libc; #[warn(clippy::cast_ptr_alignment)] -#[allow(clippy::no_effect, clippy::unnecessary_operation, clippy::cast_lossless)] +#[allow( + clippy::no_effect, + clippy::unnecessary_operation, + clippy::cast_lossless, + clippy::borrow_as_ptr +)] + fn main() { /* These should be warned against */ diff --git a/src/tools/clippy/tests/ui/cast_alignment.stderr b/src/tools/clippy/tests/ui/cast_alignment.stderr index 7998b787b91..aedd3684455 100644 --- a/src/tools/clippy/tests/ui/cast_alignment.stderr +++ b/src/tools/clippy/tests/ui/cast_alignment.stderr @@ -1,5 +1,5 @@ error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes) - --> $DIR/cast_alignment.rs:12:5 + --> $DIR/cast_alignment.rs:18:5 | LL | (&1u8 as *const u8) as *const u16; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,19 +7,19 @@ LL | (&1u8 as *const u8) as *const u16; = note: `-D clippy::cast-ptr-alignment` implied by `-D warnings` error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 < 2 bytes) - --> $DIR/cast_alignment.rs:13:5 + --> $DIR/cast_alignment.rs:19:5 | LL | (&mut 1u8 as *mut u8) as *mut u16; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes) - --> $DIR/cast_alignment.rs:16:5 + --> $DIR/cast_alignment.rs:22:5 | LL | (&1u8 as *const u8).cast::<u16>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 < 2 bytes) - --> $DIR/cast_alignment.rs:17:5 + --> $DIR/cast_alignment.rs:23:5 | LL | (&mut 1u8 as *mut u8).cast::<u16>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/cast_ref_to_mut.rs b/src/tools/clippy/tests/ui/cast_ref_to_mut.rs index 089e5cfabe4..c48a734ba32 100644 --- a/src/tools/clippy/tests/ui/cast_ref_to_mut.rs +++ b/src/tools/clippy/tests/ui/cast_ref_to_mut.rs @@ -1,5 +1,5 @@ #![warn(clippy::cast_ref_to_mut)] -#![allow(clippy::no_effect)] +#![allow(clippy::no_effect, clippy::borrow_as_ptr)] extern "C" { // N.B., mutability can be easily incorrect in FFI calls -- as diff --git a/src/tools/clippy/tests/ui/deref_addrof.fixed b/src/tools/clippy/tests/ui/deref_addrof.fixed index 0029fc673f1..2f489deb1ee 100644 --- a/src/tools/clippy/tests/ui/deref_addrof.fixed +++ b/src/tools/clippy/tests/ui/deref_addrof.fixed @@ -37,6 +37,8 @@ fn main() { let b = &a; let b = *aref; + + let _ = unsafe { *core::ptr::addr_of!(a) }; } #[rustfmt::skip] diff --git a/src/tools/clippy/tests/ui/deref_addrof.rs b/src/tools/clippy/tests/ui/deref_addrof.rs index f2f02dd5e72..49f360b9a7f 100644 --- a/src/tools/clippy/tests/ui/deref_addrof.rs +++ b/src/tools/clippy/tests/ui/deref_addrof.rs @@ -37,6 +37,8 @@ fn main() { let b = *&&a; let b = **&aref; + + let _ = unsafe { *core::ptr::addr_of!(a) }; } #[rustfmt::skip] diff --git a/src/tools/clippy/tests/ui/deref_addrof.stderr b/src/tools/clippy/tests/ui/deref_addrof.stderr index 5bc1cbfa215..75371fcdb96 100644 --- a/src/tools/clippy/tests/ui/deref_addrof.stderr +++ b/src/tools/clippy/tests/ui/deref_addrof.stderr @@ -49,7 +49,7 @@ LL | let b = **&aref; | ^^^^^^ help: try this: `aref` error: immediately dereferencing a reference - --> $DIR/deref_addrof.rs:45:9 + --> $DIR/deref_addrof.rs:47:9 | LL | *& $visitor | ^^^^^^^^^^^ help: try this: `$visitor` @@ -60,7 +60,7 @@ LL | m!(self) = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) error: immediately dereferencing a reference - --> $DIR/deref_addrof.rs:52:9 + --> $DIR/deref_addrof.rs:54:9 | LL | *& mut $visitor | ^^^^^^^^^^^^^^^ help: try this: `$visitor` diff --git a/src/tools/clippy/tests/ui/eq_op_macros.stderr b/src/tools/clippy/tests/ui/eq_op_macros.stderr index 885415b42c7..cd9f1826e59 100644 --- a/src/tools/clippy/tests/ui/eq_op_macros.stderr +++ b/src/tools/clippy/tests/ui/eq_op_macros.stderr @@ -21,6 +21,28 @@ LL | assert_in_macro_def!(); | = note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info) +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op_macros.rs:9:26 + | +LL | debug_assert_eq!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ---------------------- in this macro invocation + | + = note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op_macros.rs:10:26 + | +LL | debug_assert_ne!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ---------------------- in this macro invocation + | + = note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info) + error: identical args used in this `assert_eq!` macro call --> $DIR/eq_op_macros.rs:22:16 | @@ -46,28 +68,6 @@ LL | assert_ne!(a + 1, a + 1); | ^^^^^^^^^^^^ error: identical args used in this `debug_assert_eq!` macro call - --> $DIR/eq_op_macros.rs:9:26 - | -LL | debug_assert_eq!(a, a); - | ^^^^ -... -LL | assert_in_macro_def!(); - | ---------------------- in this macro invocation - | - = note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: identical args used in this `debug_assert_ne!` macro call - --> $DIR/eq_op_macros.rs:10:26 - | -LL | debug_assert_ne!(a, a); - | ^^^^ -... -LL | assert_in_macro_def!(); - | ---------------------- in this macro invocation - | - = note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: identical args used in this `debug_assert_eq!` macro call --> $DIR/eq_op_macros.rs:38:22 | LL | debug_assert_eq!(a, a); diff --git a/src/tools/clippy/tests/ui/erasing_op.rs b/src/tools/clippy/tests/ui/erasing_op.rs index 1540062a4bc..ae2fad0086d 100644 --- a/src/tools/clippy/tests/ui/erasing_op.rs +++ b/src/tools/clippy/tests/ui/erasing_op.rs @@ -1,3 +1,34 @@ +struct Length(u8); +struct Meter; + +impl core::ops::Mul<Meter> for u8 { + type Output = Length; + fn mul(self, _: Meter) -> Length { + Length(self) + } +} + +#[derive(Clone, Default, PartialEq, Eq, Hash)] +struct Vec1 { + x: i32, +} + +impl core::ops::Mul<Vec1> for i32 { + type Output = Vec1; + fn mul(self, mut right: Vec1) -> Vec1 { + right.x *= self; + right + } +} + +impl core::ops::Mul<i32> for Vec1 { + type Output = Vec1; + fn mul(mut self, right: i32) -> Vec1 { + self.x *= right; + self + } +} + #[allow(clippy::no_effect)] #[warn(clippy::erasing_op)] fn main() { @@ -6,4 +37,7 @@ fn main() { x * 0; 0 & x; 0 / x; + 0 * Meter; // no error: Output type is different from the non-zero argument + 0 * Vec1 { x: 5 }; + Vec1 { x: 5 } * 0; } diff --git a/src/tools/clippy/tests/ui/erasing_op.stderr b/src/tools/clippy/tests/ui/erasing_op.stderr index e54ce85f98e..165ed9bfe58 100644 --- a/src/tools/clippy/tests/ui/erasing_op.stderr +++ b/src/tools/clippy/tests/ui/erasing_op.stderr @@ -1,5 +1,5 @@ error: this operation will always return zero. This is likely not the intended outcome - --> $DIR/erasing_op.rs:6:5 + --> $DIR/erasing_op.rs:37:5 | LL | x * 0; | ^^^^^ @@ -7,16 +7,28 @@ LL | x * 0; = note: `-D clippy::erasing-op` implied by `-D warnings` error: this operation will always return zero. This is likely not the intended outcome - --> $DIR/erasing_op.rs:7:5 + --> $DIR/erasing_op.rs:38:5 | LL | 0 & x; | ^^^^^ error: this operation will always return zero. This is likely not the intended outcome - --> $DIR/erasing_op.rs:8:5 + --> $DIR/erasing_op.rs:39:5 | LL | 0 / x; | ^^^^^ -error: aborting due to 3 previous errors +error: this operation will always return zero. This is likely not the intended outcome + --> $DIR/erasing_op.rs:41:5 + | +LL | 0 * Vec1 { x: 5 }; + | ^^^^^^^^^^^^^^^^^ + +error: this operation will always return zero. This is likely not the intended outcome + --> $DIR/erasing_op.rs:42:5 + | +LL | Vec1 { x: 5 } * 0; + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/eta.fixed b/src/tools/clippy/tests/ui/eta.fixed index 1de79667f55..f938f710688 100644 --- a/src/tools/clippy/tests/ui/eta.fixed +++ b/src/tools/clippy/tests/ui/eta.fixed @@ -248,3 +248,14 @@ mod type_param_bound { take(X::fun as fn()); } } + +// #8073 Don't replace closure with `Arc<F>` or `Rc<F>` +fn arc_fp() { + let rc = std::rc::Rc::new(|| 7); + let arc = std::sync::Arc::new(|n| n + 1); + let ref_arc = &std::sync::Arc::new(|_| 5); + + true.then(|| rc()); + (0..5).map(|n| arc(n)); + Some(4).map(|n| ref_arc(n)); +} diff --git a/src/tools/clippy/tests/ui/eta.rs b/src/tools/clippy/tests/ui/eta.rs index 86abd347baa..075bbc74922 100644 --- a/src/tools/clippy/tests/ui/eta.rs +++ b/src/tools/clippy/tests/ui/eta.rs @@ -248,3 +248,14 @@ mod type_param_bound { take(X::fun as fn()); } } + +// #8073 Don't replace closure with `Arc<F>` or `Rc<F>` +fn arc_fp() { + let rc = std::rc::Rc::new(|| 7); + let arc = std::sync::Arc::new(|n| n + 1); + let ref_arc = &std::sync::Arc::new(|_| 5); + + true.then(|| rc()); + (0..5).map(|n| arc(n)); + Some(4).map(|n| ref_arc(n)); +} diff --git a/src/tools/clippy/tests/ui/identity_op.rs b/src/tools/clippy/tests/ui/identity_op.rs index 2ed4b5db574..12bbda71f43 100644 --- a/src/tools/clippy/tests/ui/identity_op.rs +++ b/src/tools/clippy/tests/ui/identity_op.rs @@ -11,6 +11,17 @@ impl std::ops::Shl<i32> for A { self } } + +struct Length(u8); +struct Meter; + +impl core::ops::Mul<Meter> for u8 { + type Output = Length; + fn mul(self, _: Meter) -> Length { + Length(self) + } +} + #[allow( clippy::eq_op, clippy::no_effect, @@ -53,4 +64,6 @@ fn main() { let mut a = A("".into()); let b = a << 0; // no error: non-integer + + 1 * Meter; // no error: non-integer } diff --git a/src/tools/clippy/tests/ui/identity_op.stderr b/src/tools/clippy/tests/ui/identity_op.stderr index ff34b38db01..0103cf5457e 100644 --- a/src/tools/clippy/tests/ui/identity_op.stderr +++ b/src/tools/clippy/tests/ui/identity_op.stderr @@ -1,5 +1,5 @@ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:26:5 + --> $DIR/identity_op.rs:37:5 | LL | x + 0; | ^^^^^ @@ -7,73 +7,73 @@ LL | x + 0; = note: `-D clippy::identity-op` implied by `-D warnings` error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:27:5 + --> $DIR/identity_op.rs:38:5 | LL | x + (1 - 1); | ^^^^^^^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:29:5 + --> $DIR/identity_op.rs:40:5 | LL | 0 + x; | ^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:32:5 + --> $DIR/identity_op.rs:43:5 | LL | x | (0); | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:35:5 + --> $DIR/identity_op.rs:46:5 | LL | x * 1; | ^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:36:5 + --> $DIR/identity_op.rs:47:5 | LL | 1 * x; | ^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:42:5 + --> $DIR/identity_op.rs:53:5 | LL | -1 & x; | ^^^^^^ error: the operation is ineffective. Consider reducing it to `u` - --> $DIR/identity_op.rs:45:5 + --> $DIR/identity_op.rs:56:5 | LL | u & 255; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `42` - --> $DIR/identity_op.rs:48:5 + --> $DIR/identity_op.rs:59:5 | LL | 42 << 0; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:49:5 + --> $DIR/identity_op.rs:60:5 | LL | 1 >> 0; | ^^^^^^ error: the operation is ineffective. Consider reducing it to `42` - --> $DIR/identity_op.rs:50:5 + --> $DIR/identity_op.rs:61:5 | LL | 42 >> 0; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `&x` - --> $DIR/identity_op.rs:51:5 + --> $DIR/identity_op.rs:62:5 | LL | &x >> 0; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:52:5 + --> $DIR/identity_op.rs:63:5 | LL | x >> &0; | ^^^^^^^ diff --git a/src/tools/clippy/tests/ui/issue_4266.stderr b/src/tools/clippy/tests/ui/issue_4266.stderr index 20419457b47..e5042aaa776 100644 --- a/src/tools/clippy/tests/ui/issue_4266.stderr +++ b/src/tools/clippy/tests/ui/issue_4266.stderr @@ -12,5 +12,14 @@ error: explicit lifetimes given in parameter types where they could be elided (o LL | async fn one_to_one<'a>(s: &'a str) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: methods called `new` usually take no `self` + --> $DIR/issue_4266.rs:27:22 + | +LL | pub async fn new(&mut self) -> Self { + | ^^^^^^^^^ + | + = note: `-D clippy::wrong-self-convention` implied by `-D warnings` + = help: consider choosing a less ambiguous name + +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/iter_not_returning_iterator.rs b/src/tools/clippy/tests/ui/iter_not_returning_iterator.rs index 377f760b3c4..2c91e02e842 100644 --- a/src/tools/clippy/tests/ui/iter_not_returning_iterator.rs +++ b/src/tools/clippy/tests/ui/iter_not_returning_iterator.rs @@ -44,4 +44,24 @@ impl Iterator for Counter { } } +// Issue #8225 +trait Iter { + type I; + fn iter(&self) -> Self::I; +} + +impl Iter for () { + type I = core::slice::Iter<'static, ()>; + fn iter(&self) -> Self::I { + [].iter() + } +} + +struct S; +impl S { + fn iter(&self) -> <() as Iter>::I { + ().iter() + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/iter_not_returning_iterator.stderr b/src/tools/clippy/tests/ui/iter_not_returning_iterator.stderr index 2273cd0be66..44f02955836 100644 --- a/src/tools/clippy/tests/ui/iter_not_returning_iterator.stderr +++ b/src/tools/clippy/tests/ui/iter_not_returning_iterator.stderr @@ -12,5 +12,11 @@ error: this method is named `iter_mut` but its return type does not implement `I LL | fn iter_mut(&self) -> Counter2 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: this method is named `iter` but its return type does not implement `Iterator` + --> $DIR/iter_not_returning_iterator.rs:50:5 + | +LL | fn iter(&self) -> Self::I; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/macro_use_imports.fixed b/src/tools/clippy/tests/ui/macro_use_imports.fixed index 9171558f3a2..306ea50258d 100644 --- a/src/tools/clippy/tests/ui/macro_use_imports.fixed +++ b/src/tools/clippy/tests/ui/macro_use_imports.fixed @@ -40,7 +40,7 @@ mod a { } } -// issue #7015, ICE due to calling `item_children` with local `DefId` +// issue #7015, ICE due to calling `module_children` with local `DefId` #[macro_use] use a as b; diff --git a/src/tools/clippy/tests/ui/macro_use_imports.rs b/src/tools/clippy/tests/ui/macro_use_imports.rs index cd01fd43f6d..e26a7545ea6 100644 --- a/src/tools/clippy/tests/ui/macro_use_imports.rs +++ b/src/tools/clippy/tests/ui/macro_use_imports.rs @@ -40,7 +40,7 @@ mod a { } } -// issue #7015, ICE due to calling `item_children` with local `DefId` +// issue #7015, ICE due to calling `module_children` with local `DefId` #[macro_use] use a as b; diff --git a/src/tools/clippy/tests/ui/manual_bits.fixed b/src/tools/clippy/tests/ui/manual_bits.fixed new file mode 100644 index 00000000000..4f1b19b75b8 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_bits.fixed @@ -0,0 +1,48 @@ +// run-rustfix + +#![warn(clippy::manual_bits)] +#![allow(clippy::no_effect, path_statements, unused_must_use, clippy::unnecessary_operation)] + +use std::mem::{size_of, size_of_val}; + +fn main() { + i8::BITS; + i16::BITS; + i32::BITS; + i64::BITS; + i128::BITS; + isize::BITS; + + u8::BITS; + u16::BITS; + u32::BITS; + u64::BITS; + u128::BITS; + usize::BITS; + + i8::BITS; + i16::BITS; + i32::BITS; + i64::BITS; + i128::BITS; + isize::BITS; + + u8::BITS; + u16::BITS; + u32::BITS; + u64::BITS; + u128::BITS; + usize::BITS; + + size_of::<usize>() * 4; + 4 * size_of::<usize>(); + size_of::<bool>() * 8; + 8 * size_of::<bool>(); + + size_of_val(&0u32) * 8; + + type Word = u32; + Word::BITS; + type Bool = bool; + size_of::<Bool>() * 8; +} diff --git a/src/tools/clippy/tests/ui/manual_bits.rs b/src/tools/clippy/tests/ui/manual_bits.rs new file mode 100644 index 00000000000..f8a01313e6a --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_bits.rs @@ -0,0 +1,48 @@ +// run-rustfix + +#![warn(clippy::manual_bits)] +#![allow(clippy::no_effect, path_statements, unused_must_use, clippy::unnecessary_operation)] + +use std::mem::{size_of, size_of_val}; + +fn main() { + size_of::<i8>() * 8; + size_of::<i16>() * 8; + size_of::<i32>() * 8; + size_of::<i64>() * 8; + size_of::<i128>() * 8; + size_of::<isize>() * 8; + + size_of::<u8>() * 8; + size_of::<u16>() * 8; + size_of::<u32>() * 8; + size_of::<u64>() * 8; + size_of::<u128>() * 8; + size_of::<usize>() * 8; + + 8 * size_of::<i8>(); + 8 * size_of::<i16>(); + 8 * size_of::<i32>(); + 8 * size_of::<i64>(); + 8 * size_of::<i128>(); + 8 * size_of::<isize>(); + + 8 * size_of::<u8>(); + 8 * size_of::<u16>(); + 8 * size_of::<u32>(); + 8 * size_of::<u64>(); + 8 * size_of::<u128>(); + 8 * size_of::<usize>(); + + size_of::<usize>() * 4; + 4 * size_of::<usize>(); + size_of::<bool>() * 8; + 8 * size_of::<bool>(); + + size_of_val(&0u32) * 8; + + type Word = u32; + size_of::<Word>() * 8; + type Bool = bool; + size_of::<Bool>() * 8; +} diff --git a/src/tools/clippy/tests/ui/manual_bits.stderr b/src/tools/clippy/tests/ui/manual_bits.stderr new file mode 100644 index 00000000000..c4f5af2dcb0 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_bits.stderr @@ -0,0 +1,154 @@ +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:9:5 + | +LL | size_of::<i8>() * 8; + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS` + | + = note: `-D clippy::manual-bits` implied by `-D warnings` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:10:5 + | +LL | size_of::<i16>() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:11:5 + | +LL | size_of::<i32>() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:12:5 + | +LL | size_of::<i64>() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:13:5 + | +LL | size_of::<i128>() * 8; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:14:5 + | +LL | size_of::<isize>() * 8; + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:16:5 + | +LL | size_of::<u8>() * 8; + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:17:5 + | +LL | size_of::<u16>() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:18:5 + | +LL | size_of::<u32>() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:19:5 + | +LL | size_of::<u64>() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:20:5 + | +LL | size_of::<u128>() * 8; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:21:5 + | +LL | size_of::<usize>() * 8; + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:23:5 + | +LL | 8 * size_of::<i8>(); + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:24:5 + | +LL | 8 * size_of::<i16>(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:25:5 + | +LL | 8 * size_of::<i32>(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:26:5 + | +LL | 8 * size_of::<i64>(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:27:5 + | +LL | 8 * size_of::<i128>(); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:28:5 + | +LL | 8 * size_of::<isize>(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:30:5 + | +LL | 8 * size_of::<u8>(); + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:31:5 + | +LL | 8 * size_of::<u16>(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:32:5 + | +LL | 8 * size_of::<u32>(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:33:5 + | +LL | 8 * size_of::<u64>(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:34:5 + | +LL | 8 * size_of::<u128>(); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:35:5 + | +LL | 8 * size_of::<usize>(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:45:5 + | +LL | size_of::<Word>() * 8; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `Word::BITS` + +error: aborting due to 25 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_memcpy/with_loop_counters.stderr b/src/tools/clippy/tests/ui/manual_memcpy/with_loop_counters.stderr index 2e3ebadd7b5..79d40c0bcb1 100644 --- a/src/tools/clippy/tests/ui/manual_memcpy/with_loop_counters.stderr +++ b/src/tools/clippy/tests/ui/manual_memcpy/with_loop_counters.stderr @@ -5,7 +5,7 @@ LL | / for i in 3..src.len() { LL | | dst[i] = src[count]; LL | | count += 1; LL | | } - | |_____^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)]);` + | |_____^ help: try replacing the loop by: `dst[3..src.len()].copy_from_slice(&src[..(src.len() - 3)]);` | = note: `-D clippy::manual-memcpy` implied by `-D warnings` @@ -16,7 +16,7 @@ LL | / for i in 3..src.len() { LL | | dst[count] = src[i]; LL | | count += 1; LL | | } - | |_____^ help: try replacing the loop by: `dst[..(src.len() - 3)].clone_from_slice(&src[3..]);` + | |_____^ help: try replacing the loop by: `dst[..(src.len() - 3)].copy_from_slice(&src[3..]);` error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:17:5 @@ -25,7 +25,7 @@ LL | / for i in 0..src.len() { LL | | dst[count] = src[i]; LL | | count += 1; LL | | } - | |_____^ help: try replacing the loop by: `dst[3..(src.len() + 3)].clone_from_slice(&src[..]);` + | |_____^ help: try replacing the loop by: `dst[3..(src.len() + 3)].copy_from_slice(&src[..]);` error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:23:5 @@ -34,7 +34,7 @@ LL | / for i in 0..src.len() { LL | | dst[i] = src[count]; LL | | count += 1; LL | | } - | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[3..(src.len() + 3)]);` + | |_____^ help: try replacing the loop by: `dst[..src.len()].copy_from_slice(&src[3..(src.len() + 3)]);` error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:29:5 @@ -43,7 +43,7 @@ LL | / for i in 3..(3 + src.len()) { LL | | dst[i] = src[count]; LL | | count += 1; LL | | } - | |_____^ help: try replacing the loop by: `dst[3..(3 + src.len())].clone_from_slice(&src[..(3 + src.len() - 3)]);` + | |_____^ help: try replacing the loop by: `dst[3..(3 + src.len())].copy_from_slice(&src[..(3 + src.len() - 3)]);` error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:35:5 @@ -52,7 +52,7 @@ LL | / for i in 5..src.len() { LL | | dst[i] = src[count - 2]; LL | | count += 1; LL | | } - | |_____^ help: try replacing the loop by: `dst[5..src.len()].clone_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)]);` + | |_____^ help: try replacing the loop by: `dst[5..src.len()].copy_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)]);` error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:41:5 @@ -61,7 +61,7 @@ LL | / for i in 0..dst.len() { LL | | dst[i] = src[count]; LL | | count += 1; LL | | } - | |_____^ help: try replacing the loop by: `dst.clone_from_slice(&src[2..(dst.len() + 2)]);` + | |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[2..(dst.len() + 2)]);` error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:47:5 @@ -70,7 +70,7 @@ LL | / for i in 3..10 { LL | | dst[i] = src[count]; LL | | count += 1; LL | | } - | |_____^ help: try replacing the loop by: `dst[3..10].clone_from_slice(&src[5..(10 + 5 - 3)]);` + | |_____^ help: try replacing the loop by: `dst[3..10].copy_from_slice(&src[5..(10 + 5 - 3)]);` error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:54:5 @@ -85,8 +85,8 @@ LL | | } | help: try replacing the loop by | -LL ~ dst[3..(src.len() + 3)].clone_from_slice(&src[..]); -LL + dst2[30..(src.len() + 30)].clone_from_slice(&src[..]); +LL ~ dst[3..(src.len() + 3)].copy_from_slice(&src[..]); +LL + dst2[30..(src.len() + 30)].copy_from_slice(&src[..]); | error: it looks like you're manually copying between slices @@ -96,7 +96,7 @@ LL | / for i in 0..1 << 1 { LL | | dst[count] = src[i + 2]; LL | | count += 1; LL | | } - | |_____^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)]);` + | |_____^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].copy_from_slice(&src[2..((1 << 1) + 2)]);` error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:71:5 @@ -105,7 +105,7 @@ LL | / for i in 3..src.len() { LL | | dst[i] = src[count]; LL | | count += 1 LL | | } - | |_____^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)]);` + | |_____^ help: try replacing the loop by: `dst[3..src.len()].copy_from_slice(&src[..(src.len() - 3)]);` error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.rs b/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.rs index 0083f94798f..ea0535d076b 100644 --- a/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.rs +++ b/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.rs @@ -113,6 +113,17 @@ pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { for i in 0.. { dst[i] = src[i]; } + + // VecDeque - ideally this would work, but would require something like `range_as_slices` + let mut dst = std::collections::VecDeque::from_iter([0; 5]); + let src = std::collections::VecDeque::from_iter([0, 1, 2, 3, 4]); + for i in 0..dst.len() { + dst[i] = src[i]; + } + let src = vec![0, 1, 2, 3, 4]; + for i in 0..dst.len() { + dst[i] = src[i]; + } } #[warn(clippy::needless_range_loop, clippy::manual_memcpy)] diff --git a/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.stderr b/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.stderr index 8ff0137a812..c163ae061df 100644 --- a/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.stderr +++ b/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.stderr @@ -4,7 +4,7 @@ error: it looks like you're manually copying between slices LL | / for i in 0..src.len() { LL | | dst[i] = src[i]; LL | | } - | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..]);` + | |_____^ help: try replacing the loop by: `dst[..src.len()].copy_from_slice(&src[..]);` | = note: `-D clippy::manual-memcpy` implied by `-D warnings` @@ -14,7 +14,7 @@ error: it looks like you're manually copying between slices LL | / for i in 0..src.len() { LL | | dst[i + 10] = src[i]; LL | | } - | |_____^ help: try replacing the loop by: `dst[10..(src.len() + 10)].clone_from_slice(&src[..]);` + | |_____^ help: try replacing the loop by: `dst[10..(src.len() + 10)].copy_from_slice(&src[..]);` error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:17:5 @@ -22,7 +22,7 @@ error: it looks like you're manually copying between slices LL | / for i in 0..src.len() { LL | | dst[i] = src[i + 10]; LL | | } - | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[10..(src.len() + 10)]);` + | |_____^ help: try replacing the loop by: `dst[..src.len()].copy_from_slice(&src[10..(src.len() + 10)]);` error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:22:5 @@ -30,7 +30,7 @@ error: it looks like you're manually copying between slices LL | / for i in 11..src.len() { LL | | dst[i] = src[i - 10]; LL | | } - | |_____^ help: try replacing the loop by: `dst[11..src.len()].clone_from_slice(&src[(11 - 10)..(src.len() - 10)]);` + | |_____^ help: try replacing the loop by: `dst[11..src.len()].copy_from_slice(&src[(11 - 10)..(src.len() - 10)]);` error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:27:5 @@ -38,7 +38,7 @@ error: it looks like you're manually copying between slices LL | / for i in 0..dst.len() { LL | | dst[i] = src[i]; LL | | } - | |_____^ help: try replacing the loop by: `dst.clone_from_slice(&src[..dst.len()]);` + | |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[..dst.len()]);` error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:40:5 @@ -51,8 +51,8 @@ LL | | } | help: try replacing the loop by | -LL ~ dst[10..256].clone_from_slice(&src[(10 - 5)..(256 - 5)]); -LL + dst2[(10 + 500)..(256 + 500)].clone_from_slice(&src[10..256]); +LL ~ dst[10..256].copy_from_slice(&src[(10 - 5)..(256 - 5)]); +LL + dst2[(10 + 500)..(256 + 500)].copy_from_slice(&src[10..256]); | error: it looks like you're manually copying between slices @@ -61,7 +61,7 @@ error: it looks like you're manually copying between slices LL | / for i in 10..LOOP_OFFSET { LL | | dst[i + LOOP_OFFSET] = src[i - some_var]; LL | | } - | |_____^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].clone_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)]);` + | |_____^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].copy_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)]);` error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:65:5 @@ -69,7 +69,7 @@ error: it looks like you're manually copying between slices LL | / for i in 0..src_vec.len() { LL | | dst_vec[i] = src_vec[i]; LL | | } - | |_____^ help: try replacing the loop by: `dst_vec[..src_vec.len()].clone_from_slice(&src_vec[..]);` + | |_____^ help: try replacing the loop by: `dst_vec[..src_vec.len()].copy_from_slice(&src_vec[..]);` error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:94:5 @@ -77,7 +77,7 @@ error: it looks like you're manually copying between slices LL | / for i in from..from + src.len() { LL | | dst[i] = src[i - from]; LL | | } - | |_____^ help: try replacing the loop by: `dst[from..(from + src.len())].clone_from_slice(&src[..(from + src.len() - from)]);` + | |_____^ help: try replacing the loop by: `dst[from..(from + src.len())].copy_from_slice(&src[..(from + src.len() - from)]);` error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:98:5 @@ -85,7 +85,7 @@ error: it looks like you're manually copying between slices LL | / for i in from..from + 3 { LL | | dst[i] = src[i - from]; LL | | } - | |_____^ help: try replacing the loop by: `dst[from..(from + 3)].clone_from_slice(&src[..(from + 3 - from)]);` + | |_____^ help: try replacing the loop by: `dst[from..(from + 3)].copy_from_slice(&src[..(from + 3 - from)]);` error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:103:5 @@ -93,7 +93,7 @@ error: it looks like you're manually copying between slices LL | / for i in 0..5 { LL | | dst[i - 0] = src[i]; LL | | } - | |_____^ help: try replacing the loop by: `dst[..5].clone_from_slice(&src[..5]);` + | |_____^ help: try replacing the loop by: `dst[..5].copy_from_slice(&src[..5]);` error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:108:5 @@ -101,10 +101,10 @@ error: it looks like you're manually copying between slices LL | / for i in 0..0 { LL | | dst[i] = src[i]; LL | | } - | |_____^ help: try replacing the loop by: `dst[..0].clone_from_slice(&src[..0]);` + | |_____^ help: try replacing the loop by: `dst[..0].copy_from_slice(&src[..0]);` error: it looks like you're manually copying between slices - --> $DIR/without_loop_counters.rs:120:5 + --> $DIR/without_loop_counters.rs:131:5 | LL | / for i in 0..src.len() { LL | | dst[i] = src[i].clone(); diff --git a/src/tools/clippy/tests/ui/missing_panics_doc.stderr b/src/tools/clippy/tests/ui/missing_panics_doc.stderr index 8bccbaefe23..91ebd695238 100644 --- a/src/tools/clippy/tests/ui/missing_panics_doc.stderr +++ b/src/tools/clippy/tests/ui/missing_panics_doc.stderr @@ -27,7 +27,6 @@ note: first possible panic found here | LL | panic!("This function panics") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info) error: docs for function which may panic missing `# Panics` section --> $DIR/missing_panics_doc.rs:17:1 @@ -42,7 +41,6 @@ note: first possible panic found here | LL | todo!() | ^^^^^^^ - = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error: docs for function which may panic missing `# Panics` section --> $DIR/missing_panics_doc.rs:22:1 @@ -61,7 +59,6 @@ note: first possible panic found here | LL | panic!() | ^^^^^^^^ - = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info) error: docs for function which may panic missing `# Panics` section --> $DIR/missing_panics_doc.rs:31:1 @@ -76,7 +73,6 @@ note: first possible panic found here | LL | if true { unreachable!() } else { panic!() } | ^^^^^^^^ - = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info) error: docs for function which may panic missing `# Panics` section --> $DIR/missing_panics_doc.rs:36:1 @@ -92,7 +88,6 @@ note: first possible panic found here | LL | assert_eq!(x, 0); | ^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: docs for function which may panic missing `# Panics` section --> $DIR/missing_panics_doc.rs:42:1 @@ -108,7 +103,6 @@ note: first possible panic found here | LL | assert_ne!(x, 0); | ^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `assert_ne` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/mutex_atomic.rs b/src/tools/clippy/tests/ui/mutex_atomic.rs index b9d78b7f479..47b3dad3989 100644 --- a/src/tools/clippy/tests/ui/mutex_atomic.rs +++ b/src/tools/clippy/tests/ui/mutex_atomic.rs @@ -1,5 +1,7 @@ #![warn(clippy::all)] #![warn(clippy::mutex_integer)] +#![warn(clippy::mutex_atomic)] +#![allow(clippy::borrow_as_ptr)] fn main() { use std::sync::Mutex; diff --git a/src/tools/clippy/tests/ui/mutex_atomic.stderr b/src/tools/clippy/tests/ui/mutex_atomic.stderr index a3511ba708a..262028a8723 100644 --- a/src/tools/clippy/tests/ui/mutex_atomic.stderr +++ b/src/tools/clippy/tests/ui/mutex_atomic.stderr @@ -1,5 +1,5 @@ error: consider using an `AtomicBool` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> $DIR/mutex_atomic.rs:6:5 + --> $DIR/mutex_atomic.rs:8:5 | LL | Mutex::new(true); | ^^^^^^^^^^^^^^^^ @@ -7,31 +7,31 @@ LL | Mutex::new(true); = note: `-D clippy::mutex-atomic` implied by `-D warnings` error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> $DIR/mutex_atomic.rs:7:5 + --> $DIR/mutex_atomic.rs:9:5 | LL | Mutex::new(5usize); | ^^^^^^^^^^^^^^^^^^ error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> $DIR/mutex_atomic.rs:8:5 + --> $DIR/mutex_atomic.rs:10:5 | LL | Mutex::new(9isize); | ^^^^^^^^^^^^^^^^^^ error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> $DIR/mutex_atomic.rs:10:5 + --> $DIR/mutex_atomic.rs:12:5 | LL | Mutex::new(&x as *const u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> $DIR/mutex_atomic.rs:11:5 + --> $DIR/mutex_atomic.rs:13:5 | LL | Mutex::new(&mut x as *mut u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> $DIR/mutex_atomic.rs:12:5 + --> $DIR/mutex_atomic.rs:14:5 | LL | Mutex::new(0u32); | ^^^^^^^^^^^^^^^^ @@ -39,7 +39,7 @@ LL | Mutex::new(0u32); = note: `-D clippy::mutex-integer` implied by `-D warnings` error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> $DIR/mutex_atomic.rs:13:5 + --> $DIR/mutex_atomic.rs:15:5 | LL | Mutex::new(0i32); | ^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/or_fun_call.fixed b/src/tools/clippy/tests/ui/or_fun_call.fixed index d6d6ab49734..87cdb3ace47 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.fixed +++ b/src/tools/clippy/tests/ui/or_fun_call.fixed @@ -2,7 +2,7 @@ #![warn(clippy::or_fun_call)] #![allow(dead_code)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::unnecessary_wraps, clippy::borrow_as_ptr)] use std::collections::BTreeMap; use std::collections::HashMap; diff --git a/src/tools/clippy/tests/ui/or_fun_call.rs b/src/tools/clippy/tests/ui/or_fun_call.rs index 8eadc6ce3b4..3f69cef301c 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.rs +++ b/src/tools/clippy/tests/ui/or_fun_call.rs @@ -2,7 +2,7 @@ #![warn(clippy::or_fun_call)] #![allow(dead_code)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::unnecessary_wraps, clippy::borrow_as_ptr)] use std::collections::BTreeMap; use std::collections::HashMap; diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn.stderr b/src/tools/clippy/tests/ui/panic_in_result_fn.stderr index 78d09b8b210..561503ae54f 100644 --- a/src/tools/clippy/tests/ui/panic_in_result_fn.stderr +++ b/src/tools/clippy/tests/ui/panic_in_result_fn.stderr @@ -14,7 +14,6 @@ note: return Err() instead of panicking | LL | panic!("error"); | ^^^^^^^^^^^^^^^ - = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:11:5 @@ -31,7 +30,6 @@ note: return Err() instead of panicking | LL | unimplemented!(); | ^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:16:5 @@ -48,7 +46,6 @@ note: return Err() instead of panicking | LL | unreachable!(); | ^^^^^^^^^^^^^^ - = note: this error originates in the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:21:5 @@ -65,7 +62,6 @@ note: return Err() instead of panicking | LL | todo!("Finish this"); | ^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:52:1 @@ -82,7 +78,6 @@ note: return Err() instead of panicking | LL | panic!("error"); | ^^^^^^^^^^^^^^^ - = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:67:1 @@ -99,7 +94,6 @@ note: return Err() instead of panicking | LL | todo!("finish main method"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.stderr b/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.stderr index 7501d6d85ed..b6aa005e7b5 100644 --- a/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.stderr +++ b/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.stderr @@ -15,7 +15,6 @@ note: return Err() instead of panicking | LL | assert!(x == 5, "wrong argument"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn_assertions.rs:13:5 @@ -33,7 +32,6 @@ note: return Err() instead of panicking | LL | assert_eq!(x, 5); | ^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn_assertions.rs:19:5 @@ -51,7 +49,6 @@ note: return Err() instead of panicking | LL | assert_ne!(x, 1); | ^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `assert_ne` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/panicking_macros.stderr b/src/tools/clippy/tests/ui/panicking_macros.stderr index 2b607ff5888..bfd1c7a3801 100644 --- a/src/tools/clippy/tests/ui/panicking_macros.stderr +++ b/src/tools/clippy/tests/ui/panicking_macros.stderr @@ -25,23 +25,18 @@ LL | todo!(); | ^^^^^^^ | = note: `-D clippy::todo` implied by `-D warnings` - = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error: `todo` should not be present in production code --> $DIR/panicking_macros.rs:17:5 | LL | todo!("message"); | ^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error: `todo` should not be present in production code --> $DIR/panicking_macros.rs:18:5 | LL | todo!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error: `unimplemented` should not be present in production code --> $DIR/panicking_macros.rs:24:5 @@ -50,23 +45,18 @@ LL | unimplemented!(); | ^^^^^^^^^^^^^^^^ | = note: `-D clippy::unimplemented` implied by `-D warnings` - = note: this error originates in the macro `unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info) error: `unimplemented` should not be present in production code --> $DIR/panicking_macros.rs:25:5 | LL | unimplemented!("message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info) error: `unimplemented` should not be present in production code --> $DIR/panicking_macros.rs:26:5 | LL | unimplemented!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `unreachable!` macro --> $DIR/panicking_macros.rs:32:5 @@ -75,23 +65,18 @@ LL | unreachable!(); | ^^^^^^^^^^^^^^ | = note: `-D clippy::unreachable` implied by `-D warnings` - = note: this error originates in the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `unreachable!` macro --> $DIR/panicking_macros.rs:33:5 | LL | unreachable!("message"); | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `$crate::unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `unreachable!` macro --> $DIR/panicking_macros.rs:34:5 | LL | unreachable!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) error: `panic` should not be present in production code --> $DIR/panicking_macros.rs:40:5 @@ -104,24 +89,18 @@ error: `todo` should not be present in production code | LL | todo!(); | ^^^^^^^ - | - = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error: `unimplemented` should not be present in production code --> $DIR/panicking_macros.rs:42:5 | LL | unimplemented!(); | ^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `unreachable!` macro --> $DIR/panicking_macros.rs:43:5 | LL | unreachable!(); | ^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/return_self_not_must_use.stderr b/src/tools/clippy/tests/ui/return_self_not_must_use.stderr index 3793a5559ba..8af10cb65c4 100644 --- a/src/tools/clippy/tests/ui/return_self_not_must_use.stderr +++ b/src/tools/clippy/tests/ui/return_self_not_must_use.stderr @@ -5,6 +5,7 @@ LL | fn what(&self) -> Self; | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::return-self-not-must-use` implied by `-D warnings` + = help: consider adding the `#[must_use]` attribute to the method or directly to the `Self` type error: missing `#[must_use]` attribute on a method returning `Self` --> $DIR/return_self_not_must_use.rs:17:5 @@ -13,6 +14,8 @@ LL | / pub fn foo(&self) -> Self { LL | | Self LL | | } | |_____^ + | + = help: consider adding the `#[must_use]` attribute to the method or directly to the `Self` type error: missing `#[must_use]` attribute on a method returning `Self` --> $DIR/return_self_not_must_use.rs:20:5 @@ -21,6 +24,8 @@ LL | / pub fn bar(self) -> Self { LL | | self LL | | } | |_____^ + | + = help: consider adding the `#[must_use]` attribute to the method or directly to the `Self` type error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/single_char_lifetime_names.rs b/src/tools/clippy/tests/ui/single_char_lifetime_names.rs new file mode 100644 index 00000000000..261d8bc7260 --- /dev/null +++ b/src/tools/clippy/tests/ui/single_char_lifetime_names.rs @@ -0,0 +1,43 @@ +#![warn(clippy::single_char_lifetime_names)] + +// Lifetimes should only be linted when they're introduced +struct DiagnosticCtx<'a, 'b> +where + 'a: 'b, +{ + _source: &'a str, + _unit: &'b (), +} + +// Only the lifetimes on the `impl`'s generics should be linted +impl<'a, 'b> DiagnosticCtx<'a, 'b> { + fn new(source: &'a str, unit: &'b ()) -> DiagnosticCtx<'a, 'b> { + Self { + _source: source, + _unit: unit, + } + } +} + +// No lifetimes should be linted here +impl<'src, 'unit> DiagnosticCtx<'src, 'unit> { + fn new_pass(source: &'src str, unit: &'unit ()) -> DiagnosticCtx<'src, 'unit> { + Self { + _source: source, + _unit: unit, + } + } +} + +// Only 'a should be linted here +fn split_once<'a>(base: &'a str, other: &'_ str) -> (&'a str, Option<&'a str>) { + base.split_once(other) + .map(|(left, right)| (left, Some(right))) + .unwrap_or((base, None)) +} + +fn main() { + let src = "loop {}"; + let unit = (); + DiagnosticCtx::new(src, &unit); +} diff --git a/src/tools/clippy/tests/ui/single_char_lifetime_names.stderr b/src/tools/clippy/tests/ui/single_char_lifetime_names.stderr new file mode 100644 index 00000000000..013b64f46a8 --- /dev/null +++ b/src/tools/clippy/tests/ui/single_char_lifetime_names.stderr @@ -0,0 +1,43 @@ +error: single-character lifetime names are likely uninformative + --> $DIR/single_char_lifetime_names.rs:4:22 + | +LL | struct DiagnosticCtx<'a, 'b> + | ^^ + | + = note: `-D clippy::single-char-lifetime-names` implied by `-D warnings` + = help: use a more informative name + +error: single-character lifetime names are likely uninformative + --> $DIR/single_char_lifetime_names.rs:4:26 + | +LL | struct DiagnosticCtx<'a, 'b> + | ^^ + | + = help: use a more informative name + +error: single-character lifetime names are likely uninformative + --> $DIR/single_char_lifetime_names.rs:13:6 + | +LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> { + | ^^ + | + = help: use a more informative name + +error: single-character lifetime names are likely uninformative + --> $DIR/single_char_lifetime_names.rs:13:10 + | +LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> { + | ^^ + | + = help: use a more informative name + +error: single-character lifetime names are likely uninformative + --> $DIR/single_char_lifetime_names.rs:33:15 + | +LL | fn split_once<'a>(base: &'a str, other: &'_ str) -> (&'a str, Option<&'a str>) { + | ^^ + | + = help: use a more informative name + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/swap.fixed b/src/tools/clippy/tests/ui/swap.fixed index ef518359ec5..3329efbd4ff 100644 --- a/src/tools/clippy/tests/ui/swap.fixed +++ b/src/tools/clippy/tests/ui/swap.fixed @@ -122,3 +122,36 @@ fn main() { ; std::mem::swap(&mut c.0, &mut a); } + +fn issue_8154() { + struct S1 { + x: i32, + y: i32, + } + struct S2(S1); + struct S3<'a, 'b>(&'a mut &'b mut S1); + + impl std::ops::Deref for S2 { + type Target = S1; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl std::ops::DerefMut for S2 { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + // Don't lint. `s.0` is mutably borrowed by `s.x` and `s.y` via the deref impl. + let mut s = S2(S1 { x: 0, y: 0 }); + let t = s.x; + s.x = s.y; + s.y = t; + + // Accessing through a mutable reference is fine + let mut s = S1 { x: 0, y: 0 }; + let mut s = &mut s; + let s = S3(&mut s); + std::mem::swap(&mut s.0.x, &mut s.0.y); +} diff --git a/src/tools/clippy/tests/ui/swap.rs b/src/tools/clippy/tests/ui/swap.rs index 8518659ccf3..8179ac1f2ab 100644 --- a/src/tools/clippy/tests/ui/swap.rs +++ b/src/tools/clippy/tests/ui/swap.rs @@ -144,3 +144,38 @@ fn main() { c.0 = a; a = t; } + +fn issue_8154() { + struct S1 { + x: i32, + y: i32, + } + struct S2(S1); + struct S3<'a, 'b>(&'a mut &'b mut S1); + + impl std::ops::Deref for S2 { + type Target = S1; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl std::ops::DerefMut for S2 { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + // Don't lint. `s.0` is mutably borrowed by `s.x` and `s.y` via the deref impl. + let mut s = S2(S1 { x: 0, y: 0 }); + let t = s.x; + s.x = s.y; + s.y = t; + + // Accessing through a mutable reference is fine + let mut s = S1 { x: 0, y: 0 }; + let mut s = &mut s; + let s = S3(&mut s); + let t = s.0.x; + s.0.x = s.0.y; + s.0.y = t; +} diff --git a/src/tools/clippy/tests/ui/swap.stderr b/src/tools/clippy/tests/ui/swap.stderr index 614d16ced40..2b556b475ce 100644 --- a/src/tools/clippy/tests/ui/swap.stderr +++ b/src/tools/clippy/tests/ui/swap.stderr @@ -108,5 +108,15 @@ LL | | a = c.0; | = note: or maybe you should use `std::mem::replace`? -error: aborting due to 12 previous errors +error: this looks like you are swapping `s.0.x` and `s.0.y` manually + --> $DIR/swap.rs:178:5 + | +LL | / let t = s.0.x; +LL | | s.0.x = s.0.y; +LL | | s.0.y = t; + | |_____________^ help: try: `std::mem::swap(&mut s.0.x, &mut s.0.y)` + | + = note: or maybe you should use `std::mem::replace`? + +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs index cb2b0054e35..2edb202892a 100644 --- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs +++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs @@ -28,4 +28,49 @@ where unimplemented!(); } +trait T: Default { + fn f() + where + Self: Default; +} + +trait U: Default { + fn f() + where + Self: Clone; +} + +trait ZZ: Default { + fn g(); + fn h(); + fn f() + where + Self: Default + Clone; +} + +trait BadTrait: Default + Clone { + fn f() + where + Self: Default + Clone; + fn g() + where + Self: Default; + fn h() + where + Self: Copy; +} + +#[derive(Default, Clone)] +struct Life {} + +impl T for Life { + // this should not warn + fn f() {} +} + +impl U for Life { + // this should not warn + fn f() {} +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr index 027e1c75204..e0c7a7ec618 100644 --- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr +++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr @@ -19,5 +19,45 @@ LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z) | = help: consider removing this trait bound -error: aborting due to 2 previous errors +error: this trait bound is already specified in trait declaration + --> $DIR/trait_duplication_in_bounds.rs:34:15 + | +LL | Self: Default; + | ^^^^^^^ + | + = help: consider removing this trait bound + +error: this trait bound is already specified in trait declaration + --> $DIR/trait_duplication_in_bounds.rs:48:15 + | +LL | Self: Default + Clone; + | ^^^^^^^ + | + = help: consider removing this trait bound + +error: this trait bound is already specified in trait declaration + --> $DIR/trait_duplication_in_bounds.rs:54:15 + | +LL | Self: Default + Clone; + | ^^^^^^^ + | + = help: consider removing this trait bound + +error: this trait bound is already specified in trait declaration + --> $DIR/trait_duplication_in_bounds.rs:54:25 + | +LL | Self: Default + Clone; + | ^^^^^ + | + = help: consider removing this trait bound + +error: this trait bound is already specified in trait declaration + --> $DIR/trait_duplication_in_bounds.rs:57:15 + | +LL | Self: Default; + | ^^^^^^^ + | + = help: consider removing this trait bound + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/transmute.rs b/src/tools/clippy/tests/ui/transmute.rs index 6a7037d8f38..9b681a79aae 100644 --- a/src/tools/clippy/tests/ui/transmute.rs +++ b/src/tools/clippy/tests/ui/transmute.rs @@ -1,4 +1,4 @@ -#![allow(dead_code)] +#![allow(dead_code, clippy::borrow_as_ptr)] extern crate core; 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 9e213aab68c..f06ffab5d9b 100644 --- a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs @@ -1,4 +1,5 @@ #![warn(clippy::transmute_ptr_to_ptr)] +#![allow(clippy::borrow_as_ptr)] // 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.stderr b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.stderr index 4d1b8fcc199..49a8a3347e4 100644 --- a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.stderr +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.stderr @@ -1,5 +1,5 @@ error: transmute from a pointer to a pointer - --> $DIR/transmute_ptr_to_ptr.rs:29:29 + --> $DIR/transmute_ptr_to_ptr.rs:30:29 | LL | let _: *const f32 = std::mem::transmute(ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr as *const f32` @@ -7,31 +7,31 @@ LL | let _: *const f32 = std::mem::transmute(ptr); = note: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings` error: transmute from a pointer to a pointer - --> $DIR/transmute_ptr_to_ptr.rs:30:27 + --> $DIR/transmute_ptr_to_ptr.rs:31:27 | LL | let _: *mut f32 = std::mem::transmute(mut_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `mut_ptr as *mut f32` error: transmute from a reference to a reference - --> $DIR/transmute_ptr_to_ptr.rs:32:23 + --> $DIR/transmute_ptr_to_ptr.rs:33:23 | LL | let _: &f32 = std::mem::transmute(&1u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1u32 as *const u32 as *const f32)` error: transmute from a reference to a reference - --> $DIR/transmute_ptr_to_ptr.rs:33:23 + --> $DIR/transmute_ptr_to_ptr.rs:34:23 | LL | let _: &f64 = std::mem::transmute(&1f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1f32 as *const f32 as *const f64)` error: transmute from a reference to a reference - --> $DIR/transmute_ptr_to_ptr.rs:36:27 + --> $DIR/transmute_ptr_to_ptr.rs:37:27 | LL | let _: &mut f32 = std::mem::transmute(&mut 1u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(&mut 1u32 as *mut u32 as *mut f32)` error: transmute from a reference to a reference - --> $DIR/transmute_ptr_to_ptr.rs:37:37 + --> $DIR/transmute_ptr_to_ptr.rs:38:37 | LL | let _: &GenericParam<f32> = std::mem::transmute(&GenericParam { t: 1u32 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&GenericParam { t: 1u32 } as *const GenericParam<u32> as *const GenericParam<f32>)` 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 b6f1e83181c..9ae0ed0b13f 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 @@ -4,8 +4,7 @@ // would otherwise be responsible for #![warn(clippy::useless_transmute)] #![warn(clippy::transmute_ptr_to_ptr)] -#![allow(unused_unsafe)] -#![allow(dead_code)] +#![allow(dead_code, unused_unsafe, clippy::borrow_as_ptr)] 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 0205d1ece60..985cf9a075d 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 @@ -4,8 +4,7 @@ // would otherwise be responsible for #![warn(clippy::useless_transmute)] #![warn(clippy::transmute_ptr_to_ptr)] -#![allow(unused_unsafe)] -#![allow(dead_code)] +#![allow(dead_code, unused_unsafe, clippy::borrow_as_ptr)] use std::mem::{size_of, transmute}; diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr index 1157b179317..e8496a325d6 100644 --- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr +++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr @@ -1,5 +1,5 @@ error: transmute from an integer to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:19:39 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:18:39 | LL | let _ptr_i32_transmute = unsafe { transmute::<usize, *const i32>(usize::MAX) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `usize::MAX as *const i32` @@ -7,7 +7,7 @@ LL | let _ptr_i32_transmute = unsafe { transmute::<usize, *const i32>(usize: = note: `-D clippy::useless-transmute` implied by `-D warnings` error: transmute from a pointer to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:23:38 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:22:38 | LL | let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as *const i8` @@ -15,13 +15,13 @@ LL | let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr = note: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings` error: transmute from a pointer to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:29:46 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:28:46 | LL | let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u16]>(slice_ptr) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slice_ptr as *const [u16]` error: transmute from `*const i32` to `usize` which could be expressed as a pointer cast instead - --> $DIR/transmutes_expressible_as_ptr_casts.rs:35:50 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:34:50 | LL | let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, usize>(ptr_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as usize` @@ -29,25 +29,25 @@ LL | let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, us = note: `-D clippy::transmutes-expressible-as-ptr-casts` implied by `-D warnings` error: transmute from a reference to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:41:41 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:40:41 | LL | let _array_ptr_transmute = unsafe { transmute::<&[i32; 4], *const [i32; 4]>(array_ref) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `array_ref as *const [i32; 4]` error: transmute from `fn(usize) -> u8 {main::foo}` to `*const usize` which could be expressed as a pointer cast instead - --> $DIR/transmutes_expressible_as_ptr_casts.rs:49:41 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:48:41 | LL | let _usize_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, *const usize>(foo) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as *const usize` error: transmute from `fn(usize) -> u8 {main::foo}` to `usize` which could be expressed as a pointer cast instead - --> $DIR/transmutes_expressible_as_ptr_casts.rs:53:49 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:52:49 | LL | let _usize_from_fn_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, usize>(foo) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as usize` error: transmute from a reference to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:65:14 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:64:14 | LL | unsafe { transmute::<&[i32; 1], *const u8>(in_param) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `in_param as *const [i32; 1] as *const u8` diff --git a/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs b/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs index 766190f2099..fc740ee11d6 100644 --- a/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs +++ b/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs @@ -69,4 +69,14 @@ mod issue4326 { } } +// Issue #7360 +struct Foo<T, U> +where + T: Clone, + U: Clone, +{ + t: T, + u: U, +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/unit_cmp.stderr b/src/tools/clippy/tests/ui/unit_cmp.stderr index 2b5a7b348b9..824506a4257 100644 --- a/src/tools/clippy/tests/ui/unit_cmp.stderr +++ b/src/tools/clippy/tests/ui/unit_cmp.stderr @@ -33,8 +33,6 @@ LL | | }, LL | | } LL | | ); | |_____^ - | - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: `debug_assert_eq` of unit values detected. This will always succeed --> $DIR/unit_cmp.rs:32:5 @@ -47,8 +45,6 @@ LL | | }, LL | | } LL | | ); | |_____^ - | - = note: this error originates in the macro `$crate::assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: `assert_ne` of unit values detected. This will always fail --> $DIR/unit_cmp.rs:41:5 @@ -61,8 +57,6 @@ LL | | }, LL | | } LL | | ); | |_____^ - | - = note: this error originates in the macro `assert_ne` (in Nightly builds, run with -Z macro-backtrace for more info) error: `debug_assert_ne` of unit values detected. This will always fail --> $DIR/unit_cmp.rs:49:5 @@ -75,8 +69,6 @@ LL | | }, LL | | } LL | | ); | |_____^ - | - = note: this error originates in the macro `$crate::assert_ne` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_cast_fixable.fixed b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.fixed index bda0f2c47cd..3332f49c80c 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast_fixable.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.fixed @@ -1,7 +1,12 @@ // run-rustfix #![warn(clippy::unnecessary_cast)] -#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::nonstandard_macro_braces)] +#![allow( + clippy::no_effect, + clippy::unnecessary_operation, + clippy::nonstandard_macro_braces, + clippy::borrow_as_ptr +)] fn main() { // casting integer literal to float is unnecessary diff --git a/src/tools/clippy/tests/ui/unnecessary_cast_fixable.rs b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.rs index f7a4f2a5988..ec01e938779 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast_fixable.rs +++ b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.rs @@ -1,7 +1,12 @@ // run-rustfix #![warn(clippy::unnecessary_cast)] -#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::nonstandard_macro_braces)] +#![allow( + clippy::no_effect, + clippy::unnecessary_operation, + clippy::nonstandard_macro_braces, + clippy::borrow_as_ptr +)] fn main() { // casting integer literal to float is unnecessary diff --git a/src/tools/clippy/tests/ui/unnecessary_cast_fixable.stderr b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.stderr index 3695a8f819c..a281143281b 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast_fixable.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.stderr @@ -1,5 +1,5 @@ error: casting integer literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:8:5 + --> $DIR/unnecessary_cast_fixable.rs:13:5 | LL | 100 as f32; | ^^^^^^^^^^ help: try: `100_f32` @@ -7,97 +7,97 @@ LL | 100 as f32; = note: `-D clippy::unnecessary-cast` implied by `-D warnings` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:9:5 + --> $DIR/unnecessary_cast_fixable.rs:14:5 | LL | 100 as f64; | ^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:10:5 + --> $DIR/unnecessary_cast_fixable.rs:15:5 | LL | 100_i32 as f64; | ^^^^^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:11:13 + --> $DIR/unnecessary_cast_fixable.rs:16:13 | LL | let _ = -100 as f32; | ^^^^^^^^^^^ help: try: `-100_f32` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:12:13 + --> $DIR/unnecessary_cast_fixable.rs:17:13 | LL | let _ = -100 as f64; | ^^^^^^^^^^^ help: try: `-100_f64` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:13:13 + --> $DIR/unnecessary_cast_fixable.rs:18:13 | LL | let _ = -100_i32 as f64; | ^^^^^^^^^^^^^^^ help: try: `-100_f64` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:14:5 + --> $DIR/unnecessary_cast_fixable.rs:19:5 | LL | 100. as f32; | ^^^^^^^^^^^ help: try: `100_f32` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:15:5 + --> $DIR/unnecessary_cast_fixable.rs:20:5 | LL | 100. as f64; | ^^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:27:5 + --> $DIR/unnecessary_cast_fixable.rs:32:5 | LL | 1 as u32; | ^^^^^^^^ help: try: `1_u32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:28:5 + --> $DIR/unnecessary_cast_fixable.rs:33:5 | LL | 0x10 as i32; | ^^^^^^^^^^^ help: try: `0x10_i32` error: casting integer literal to `usize` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:29:5 + --> $DIR/unnecessary_cast_fixable.rs:34:5 | LL | 0b10 as usize; | ^^^^^^^^^^^^^ help: try: `0b10_usize` error: casting integer literal to `u16` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:30:5 + --> $DIR/unnecessary_cast_fixable.rs:35:5 | LL | 0o73 as u16; | ^^^^^^^^^^^ help: try: `0o73_u16` error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:31:5 + --> $DIR/unnecessary_cast_fixable.rs:36:5 | LL | 1_000_000_000 as u32; | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:33:5 + --> $DIR/unnecessary_cast_fixable.rs:38:5 | LL | 1.0 as f64; | ^^^^^^^^^^ help: try: `1.0_f64` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:34:5 + --> $DIR/unnecessary_cast_fixable.rs:39:5 | LL | 0.5 as f32; | ^^^^^^^^^^ help: try: `0.5_f32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:38:13 + --> $DIR/unnecessary_cast_fixable.rs:43:13 | LL | let _ = -1 as i32; | ^^^^^^^^^ help: try: `-1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:39:13 + --> $DIR/unnecessary_cast_fixable.rs:44:13 | LL | let _ = -1.0 as f32; | ^^^^^^^^^^^ help: try: `-1.0_f32` diff --git a/src/tools/clippy/tests/ui/unused_io_amount.rs b/src/tools/clippy/tests/ui/unused_io_amount.rs index 8b141e25942..4b059558173 100644 --- a/src/tools/clippy/tests/ui/unused_io_amount.rs +++ b/src/tools/clippy/tests/ui/unused_io_amount.rs @@ -1,6 +1,8 @@ #![allow(dead_code)] #![warn(clippy::unused_io_amount)] +extern crate futures; +use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; use std::io::{self, Read}; fn question_mark<T: io::Read + io::Write>(s: &mut T) -> io::Result<()> { @@ -61,4 +63,55 @@ fn combine_or(file: &str) -> Result<(), Error> { Ok(()) } +async fn bad_async_write<W: AsyncWrite + Unpin>(w: &mut W) { + w.write(b"hello world").await.unwrap(); +} + +async fn bad_async_read<R: AsyncRead + Unpin>(r: &mut R) { + let mut buf = [0u8; 0]; + r.read(&mut buf[..]).await.unwrap(); +} + +async fn io_not_ignored_async_write<W: AsyncWrite + Unpin>(mut w: W) { + // Here we're forgetting to await the future, so we should get a + // warning about _that_ (or we would, if it were enabled), but we + // won't get one about ignoring the return value. + w.write(b"hello world"); +} + +fn bad_async_write_closure<W: AsyncWrite + Unpin + 'static>(w: W) -> impl futures::Future<Output = io::Result<()>> { + let mut w = w; + async move { + w.write(b"hello world").await?; + Ok(()) + } +} + +async fn async_read_nested_or<R: AsyncRead + Unpin>(r: &mut R, do_it: bool) -> Result<[u8; 1], Error> { + let mut buf = [0u8; 1]; + if do_it { + r.read(&mut buf[..]).await.or(Err(Error::Kind))?; + } + Ok(buf) +} + +use tokio::io::{AsyncRead as TokioAsyncRead, AsyncReadExt as _, AsyncWrite as TokioAsyncWrite, AsyncWriteExt as _}; + +async fn bad_async_write_tokio<W: TokioAsyncWrite + Unpin>(w: &mut W) { + w.write(b"hello world").await.unwrap(); +} + +async fn bad_async_read_tokio<R: TokioAsyncRead + Unpin>(r: &mut R) { + let mut buf = [0u8; 0]; + r.read(&mut buf[..]).await.unwrap(); +} + +async fn undetected_bad_async_write<W: AsyncWrite + Unpin>(w: &mut W) { + // It would be good to detect this case some day, but the current lint + // doesn't handle it. (The documentation says that this lint "detects + // only common patterns".) + let future = w.write(b"Hello world"); + future.await.unwrap(); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/unused_io_amount.stderr b/src/tools/clippy/tests/ui/unused_io_amount.stderr index d8dfc0e5a79..e5bdd993aa1 100644 --- a/src/tools/clippy/tests/ui/unused_io_amount.stderr +++ b/src/tools/clippy/tests/ui/unused_io_amount.stderr @@ -1,61 +1,74 @@ -error: written amount is not handled. Use `Write::write_all` instead - --> $DIR/unused_io_amount.rs:7:5 +error: written amount is not handled + --> $DIR/unused_io_amount.rs:9:5 | LL | s.write(b"test")?; | ^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unused-io-amount` implied by `-D warnings` + = help: use `Write::write_all` instead, or handle partial writes -error: read amount is not handled. Use `Read::read_exact` instead - --> $DIR/unused_io_amount.rs:9:5 +error: read amount is not handled + --> $DIR/unused_io_amount.rs:11:5 | LL | s.read(&mut buf)?; | ^^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads -error: written amount is not handled. Use `Write::write_all` instead - --> $DIR/unused_io_amount.rs:14:5 +error: written amount is not handled + --> $DIR/unused_io_amount.rs:16:5 | LL | s.write(b"test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Write::write_all` instead, or handle partial writes -error: read amount is not handled. Use `Read::read_exact` instead - --> $DIR/unused_io_amount.rs:16:5 +error: read amount is not handled + --> $DIR/unused_io_amount.rs:18:5 | LL | s.read(&mut buf).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads error: read amount is not handled - --> $DIR/unused_io_amount.rs:20:5 + --> $DIR/unused_io_amount.rs:22:5 | LL | s.read_vectored(&mut [io::IoSliceMut::new(&mut [])])?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: written amount is not handled - --> $DIR/unused_io_amount.rs:21:5 + --> $DIR/unused_io_amount.rs:23:5 | LL | s.write_vectored(&[io::IoSlice::new(&[])])?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: read amount is not handled. Use `Read::read_exact` instead - --> $DIR/unused_io_amount.rs:28:5 +error: read amount is not handled + --> $DIR/unused_io_amount.rs:30:5 | LL | reader.read(&mut result).ok()?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads -error: read amount is not handled. Use `Read::read_exact` instead - --> $DIR/unused_io_amount.rs:37:5 +error: read amount is not handled + --> $DIR/unused_io_amount.rs:39:5 | LL | reader.read(&mut result).or_else(|err| Err(err))?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads -error: read amount is not handled. Use `Read::read_exact` instead - --> $DIR/unused_io_amount.rs:49:5 +error: read amount is not handled + --> $DIR/unused_io_amount.rs:51:5 | LL | reader.read(&mut result).or(Err(Error::Kind))?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads -error: read amount is not handled. Use `Read::read_exact` instead - --> $DIR/unused_io_amount.rs:56:5 +error: read amount is not handled + --> $DIR/unused_io_amount.rs:58:5 | LL | / reader LL | | .read(&mut result) @@ -63,6 +76,56 @@ LL | | .or(Err(Error::Kind)) LL | | .or(Err(Error::Kind)) LL | | .expect("error"); | |________________________^ + | + = help: use `Read::read_exact` instead, or handle partial reads + +error: written amount is not handled + --> $DIR/unused_io_amount.rs:67:5 + | +LL | w.write(b"hello world").await.unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `AsyncWriteExt::write_all` instead, or handle partial writes + +error: read amount is not handled + --> $DIR/unused_io_amount.rs:72:5 + | +LL | r.read(&mut buf[..]).await.unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `AsyncReadExt::read_exact` instead, or handle partial reads + +error: written amount is not handled + --> $DIR/unused_io_amount.rs:85:9 + | +LL | w.write(b"hello world").await?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `AsyncWriteExt::write_all` instead, or handle partial writes + +error: read amount is not handled + --> $DIR/unused_io_amount.rs:93:9 + | +LL | r.read(&mut buf[..]).await.or(Err(Error::Kind))?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `AsyncReadExt::read_exact` instead, or handle partial reads + +error: written amount is not handled + --> $DIR/unused_io_amount.rs:101:5 + | +LL | w.write(b"hello world").await.unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `AsyncWriteExt::write_all` instead, or handle partial writes + +error: read amount is not handled + --> $DIR/unused_io_amount.rs:106:5 + | +LL | r.read(&mut buf[..]).await.unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `AsyncReadExt::read_exact` instead, or handle partial reads -error: aborting due to 10 previous errors +error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/vtable_address_comparisons.rs b/src/tools/clippy/tests/ui/vtable_address_comparisons.rs index c91d96ee18a..a9a4a0f5a6b 100644 --- a/src/tools/clippy/tests/ui/vtable_address_comparisons.rs +++ b/src/tools/clippy/tests/ui/vtable_address_comparisons.rs @@ -4,6 +4,8 @@ use std::rc::Rc; use std::sync::Arc; #[warn(clippy::vtable_address_comparisons)] +#[allow(clippy::borrow_as_ptr)] + fn main() { let a: *const dyn Debug = &1 as &dyn Debug; let b: *const dyn Debug = &1 as &dyn Debug; diff --git a/src/tools/clippy/tests/ui/vtable_address_comparisons.stderr b/src/tools/clippy/tests/ui/vtable_address_comparisons.stderr index 76bd57217d7..2f1be61e5df 100644 --- a/src/tools/clippy/tests/ui/vtable_address_comparisons.stderr +++ b/src/tools/clippy/tests/ui/vtable_address_comparisons.stderr @@ -1,5 +1,5 @@ error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:12:13 + --> $DIR/vtable_address_comparisons.rs:14:13 | LL | let _ = a == b; | ^^^^^^ @@ -8,7 +8,7 @@ LL | let _ = a == b; = help: consider extracting and comparing data pointers only error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:13:13 + --> $DIR/vtable_address_comparisons.rs:15:13 | LL | let _ = a != b; | ^^^^^^ @@ -16,7 +16,7 @@ LL | let _ = a != b; = help: consider extracting and comparing data pointers only error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:14:13 + --> $DIR/vtable_address_comparisons.rs:16:13 | LL | let _ = a < b; | ^^^^^ @@ -24,7 +24,7 @@ LL | let _ = a < b; = help: consider extracting and comparing data pointers only error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:15:13 + --> $DIR/vtable_address_comparisons.rs:17:13 | LL | let _ = a <= b; | ^^^^^^ @@ -32,7 +32,7 @@ LL | let _ = a <= b; = help: consider extracting and comparing data pointers only error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:16:13 + --> $DIR/vtable_address_comparisons.rs:18:13 | LL | let _ = a > b; | ^^^^^ @@ -40,7 +40,7 @@ LL | let _ = a > b; = help: consider extracting and comparing data pointers only error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:17:13 + --> $DIR/vtable_address_comparisons.rs:19:13 | LL | let _ = a >= b; | ^^^^^^ @@ -48,7 +48,7 @@ LL | let _ = a >= b; = help: consider extracting and comparing data pointers only error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:18:5 + --> $DIR/vtable_address_comparisons.rs:20:5 | LL | ptr::eq(a, b); | ^^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | ptr::eq(a, b); = help: consider extracting and comparing data pointers only error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:22:5 + --> $DIR/vtable_address_comparisons.rs:24:5 | LL | ptr::eq(a, b); | ^^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | ptr::eq(a, b); = help: consider extracting and comparing data pointers only error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:25:5 + --> $DIR/vtable_address_comparisons.rs:27:5 | LL | Rc::ptr_eq(&a, &a); | ^^^^^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | Rc::ptr_eq(&a, &a); = help: consider extracting and comparing data pointers only error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:28:5 + --> $DIR/vtable_address_comparisons.rs:30:5 | LL | Arc::ptr_eq(&a, &a); | ^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/while_let_on_iterator.fixed b/src/tools/clippy/tests/ui/while_let_on_iterator.fixed index 1e74ad2de65..cb8892a3f00 100644 --- a/src/tools/clippy/tests/ui/while_let_on_iterator.fixed +++ b/src/tools/clippy/tests/ui/while_let_on_iterator.fixed @@ -372,6 +372,36 @@ fn exact_match_with_single_field() { } } +fn custom_deref() { + struct S1<T> { + x: T, + } + struct S2<T>(S1<T>); + impl<T> core::ops::Deref for S2<T> { + type Target = S1<T>; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl<T> core::ops::DerefMut for S2<T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + let mut s = S2(S1 { x: 0..10 }); + for x in s.x.by_ref() { + println!("{}", x); + } +} + +fn issue_8113() { + let mut x = [0..10]; + for x in x[0].by_ref() { + println!("{}", x); + } +} + fn main() { let mut it = 0..20; for _ in it { diff --git a/src/tools/clippy/tests/ui/while_let_on_iterator.rs b/src/tools/clippy/tests/ui/while_let_on_iterator.rs index 69cb636cee8..d9157184495 100644 --- a/src/tools/clippy/tests/ui/while_let_on_iterator.rs +++ b/src/tools/clippy/tests/ui/while_let_on_iterator.rs @@ -372,6 +372,36 @@ fn exact_match_with_single_field() { } } +fn custom_deref() { + struct S1<T> { + x: T, + } + struct S2<T>(S1<T>); + impl<T> core::ops::Deref for S2<T> { + type Target = S1<T>; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl<T> core::ops::DerefMut for S2<T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + let mut s = S2(S1 { x: 0..10 }); + while let Some(x) = s.x.next() { + println!("{}", x); + } +} + +fn issue_8113() { + let mut x = [0..10]; + while let Some(x) = x[0].next() { + println!("{}", x); + } +} + fn main() { let mut it = 0..20; while let Some(..) = it.next() { diff --git a/src/tools/clippy/tests/ui/while_let_on_iterator.stderr b/src/tools/clippy/tests/ui/while_let_on_iterator.stderr index 1a11ba26eef..fb2b0f2467c 100644 --- a/src/tools/clippy/tests/ui/while_let_on_iterator.stderr +++ b/src/tools/clippy/tests/ui/while_let_on_iterator.stderr @@ -123,10 +123,22 @@ LL | while let Some(x) = it.0.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.0.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:377:5 + --> $DIR/while_let_on_iterator.rs:393:5 + | +LL | while let Some(x) = s.x.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in s.x.by_ref()` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:400:5 + | +LL | while let Some(x) = x[0].next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in x[0].by_ref()` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:407:5 | LL | while let Some(..) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it` -error: aborting due to 21 previous errors +error: aborting due to 23 previous errors diff --git a/src/tools/clippy/tests/ui/wrong_self_convention.rs b/src/tools/clippy/tests/ui/wrong_self_convention.rs index 1b9da8a55e5..f8fee4b3ab2 100644 --- a/src/tools/clippy/tests/ui/wrong_self_convention.rs +++ b/src/tools/clippy/tests/ui/wrong_self_convention.rs @@ -188,3 +188,24 @@ mod issue6727 { } } } + +pub mod issue8142 { + struct S; + + impl S { + // Should lint: is_ methods should only take &self, or no self at all. + fn is_still_buggy(&mut self) -> bool { + false + } + + // Should not lint: "no self at all" is allowed. + fn is_forty_two(x: u32) -> bool { + x == 42 + } + + // Should not lint: &self is allowed. + fn is_test_code(&self) -> bool { + true + } + } +} diff --git a/src/tools/clippy/tests/ui/wrong_self_convention.stderr b/src/tools/clippy/tests/ui/wrong_self_convention.stderr index 590ee6d9c52..5493a99572e 100644 --- a/src/tools/clippy/tests/ui/wrong_self_convention.stderr +++ b/src/tools/clippy/tests/ui/wrong_self_convention.stderr @@ -191,5 +191,13 @@ LL | fn to_u64(self) -> u64 { | = help: consider choosing a less ambiguous name -error: aborting due to 24 previous errors +error: methods called `is_*` usually take `self` by reference or no `self` + --> $DIR/wrong_self_convention.rs:197:27 + | +LL | fn is_still_buggy(&mut self) -> bool { + | ^^^^^^^^^ + | + = help: consider choosing a less ambiguous name + +error: aborting due to 25 previous errors diff --git a/src/tools/clippy/tests/ui/zero_offset.rs b/src/tools/clippy/tests/ui/zero_offset.rs index 6c190a4c86c..fd9ac1fa766 100644 --- a/src/tools/clippy/tests/ui/zero_offset.rs +++ b/src/tools/clippy/tests/ui/zero_offset.rs @@ -1,3 +1,4 @@ +#[allow(clippy::borrow_as_ptr)] fn main() { unsafe { let m = &mut () as *mut (); diff --git a/src/tools/clippy/tests/ui/zero_offset.stderr b/src/tools/clippy/tests/ui/zero_offset.stderr index b12c8e9a73c..481a446571a 100644 --- a/src/tools/clippy/tests/ui/zero_offset.stderr +++ b/src/tools/clippy/tests/ui/zero_offset.stderr @@ -1,5 +1,5 @@ error: offset calculation on zero-sized value - --> $DIR/zero_offset.rs:4:9 + --> $DIR/zero_offset.rs:5:9 | LL | m.offset(0); | ^^^^^^^^^^^ @@ -7,43 +7,43 @@ LL | m.offset(0); = note: `#[deny(clippy::zst_offset)]` on by default error: offset calculation on zero-sized value - --> $DIR/zero_offset.rs:5:9 + --> $DIR/zero_offset.rs:6:9 | LL | m.wrapping_add(0); | ^^^^^^^^^^^^^^^^^ error: offset calculation on zero-sized value - --> $DIR/zero_offset.rs:6:9 + --> $DIR/zero_offset.rs:7:9 | LL | m.sub(0); | ^^^^^^^^ error: offset calculation on zero-sized value - --> $DIR/zero_offset.rs:7:9 + --> $DIR/zero_offset.rs:8:9 | LL | m.wrapping_sub(0); | ^^^^^^^^^^^^^^^^^ error: offset calculation on zero-sized value - --> $DIR/zero_offset.rs:10:9 + --> $DIR/zero_offset.rs:11:9 | LL | c.offset(0); | ^^^^^^^^^^^ error: offset calculation on zero-sized value - --> $DIR/zero_offset.rs:11:9 + --> $DIR/zero_offset.rs:12:9 | LL | c.wrapping_add(0); | ^^^^^^^^^^^^^^^^^ error: offset calculation on zero-sized value - --> $DIR/zero_offset.rs:12:9 + --> $DIR/zero_offset.rs:13:9 | LL | c.sub(0); | ^^^^^^^^ error: offset calculation on zero-sized value - --> $DIR/zero_offset.rs:13:9 + --> $DIR/zero_offset.rs:14:9 | LL | c.wrapping_sub(0); | ^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/workspace.rs b/src/tools/clippy/tests/workspace.rs new file mode 100644 index 00000000000..677b4a4d569 --- /dev/null +++ b/src/tools/clippy/tests/workspace.rs @@ -0,0 +1,107 @@ +#![feature(once_cell)] + +use std::path::PathBuf; +use std::process::Command; +use test_utils::{CARGO_CLIPPY_PATH, IS_RUSTC_TEST_SUITE}; + +mod test_utils; + +#[test] +fn test_no_deps_ignores_path_deps_in_workspaces() { + if IS_RUSTC_TEST_SUITE { + return; + } + let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let target_dir = root.join("target").join("workspace_test"); + let cwd = root.join("tests/workspace_test"); + + // Make sure we start with a clean state + Command::new("cargo") + .current_dir(&cwd) + .env("CARGO_TARGET_DIR", &target_dir) + .arg("clean") + .args(&["-p", "subcrate"]) + .args(&["-p", "path_dep"]) + .output() + .unwrap(); + + // `path_dep` is a path dependency of `subcrate` that would trigger a denied lint. + // Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`. + let output = Command::new(&*CARGO_CLIPPY_PATH) + .current_dir(&cwd) + .env("CARGO_INCREMENTAL", "0") + .env("CARGO_TARGET_DIR", &target_dir) + .arg("clippy") + .args(&["-p", "subcrate"]) + .arg("--no-deps") + .arg("--") + .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir + .args(&["--cfg", r#"feature="primary_package_test""#]) + .output() + .unwrap(); + println!("status: {}", output.status); + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + + assert!(output.status.success()); + + let lint_path_dep = || { + // Test that without the `--no-deps` argument, `path_dep` is linted. + let output = Command::new(&*CARGO_CLIPPY_PATH) + .current_dir(&cwd) + .env("CARGO_INCREMENTAL", "0") + .env("CARGO_TARGET_DIR", &target_dir) + .arg("clippy") + .args(&["-p", "subcrate"]) + .arg("--") + .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir + .args(&["--cfg", r#"feature="primary_package_test""#]) + .output() + .unwrap(); + println!("status: {}", output.status); + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + + assert!(!output.status.success()); + assert!( + String::from_utf8(output.stderr) + .unwrap() + .contains("error: empty `loop {}` wastes CPU cycles") + ); + }; + + // Make sure Cargo is aware of the removal of `--no-deps`. + lint_path_dep(); + + let successful_build = || { + let output = Command::new(&*CARGO_CLIPPY_PATH) + .current_dir(&cwd) + .env("CARGO_INCREMENTAL", "0") + .env("CARGO_TARGET_DIR", &target_dir) + .arg("clippy") + .args(&["-p", "subcrate"]) + .arg("--") + .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir + .output() + .unwrap(); + println!("status: {}", output.status); + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + + assert!(output.status.success()); + + output + }; + + // Trigger a sucessful build, so Cargo would like to cache the build result. + successful_build(); + + // Make sure there's no spurious rebuild when nothing changes. + let stderr = String::from_utf8(successful_build().stderr).unwrap(); + assert!(!stderr.contains("Compiling")); + assert!(!stderr.contains("Checking")); + assert!(stderr.contains("Finished")); + + // Make sure Cargo is aware of the new `--cfg` flag. + lint_path_dep(); +} diff --git a/src/tools/clippy/clippy_workspace_tests/Cargo.toml b/src/tools/clippy/tests/workspace_test/Cargo.toml index 7a235b215d3..bf5b4ca5288 100644 --- a/src/tools/clippy/clippy_workspace_tests/Cargo.toml +++ b/src/tools/clippy/tests/workspace_test/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "clippy_workspace_tests" +name = "workspace_test" version = "0.1.0" edition = "2018" diff --git a/src/tools/clippy/clippy_workspace_tests/build.rs b/src/tools/clippy/tests/workspace_test/build.rs index 3507168a3a9..3507168a3a9 100644 --- a/src/tools/clippy/clippy_workspace_tests/build.rs +++ b/src/tools/clippy/tests/workspace_test/build.rs diff --git a/src/tools/clippy/clippy_workspace_tests/path_dep/Cargo.toml b/src/tools/clippy/tests/workspace_test/path_dep/Cargo.toml index 85a91cd2dec..85a91cd2dec 100644 --- a/src/tools/clippy/clippy_workspace_tests/path_dep/Cargo.toml +++ b/src/tools/clippy/tests/workspace_test/path_dep/Cargo.toml diff --git a/src/tools/clippy/clippy_workspace_tests/path_dep/src/lib.rs b/src/tools/clippy/tests/workspace_test/path_dep/src/lib.rs index 35ce524f2b1..35ce524f2b1 100644 --- a/src/tools/clippy/clippy_workspace_tests/path_dep/src/lib.rs +++ b/src/tools/clippy/tests/workspace_test/path_dep/src/lib.rs diff --git a/src/tools/clippy/clippy_workspace_tests/src/main.rs b/src/tools/clippy/tests/workspace_test/src/main.rs index b322eca1db5..b322eca1db5 100644 --- a/src/tools/clippy/clippy_workspace_tests/src/main.rs +++ b/src/tools/clippy/tests/workspace_test/src/main.rs diff --git a/src/tools/clippy/clippy_workspace_tests/subcrate/Cargo.toml b/src/tools/clippy/tests/workspace_test/subcrate/Cargo.toml index 45362c11b85..45362c11b85 100644 --- a/src/tools/clippy/clippy_workspace_tests/subcrate/Cargo.toml +++ b/src/tools/clippy/tests/workspace_test/subcrate/Cargo.toml diff --git a/src/tools/clippy/clippy_workspace_tests/subcrate/src/lib.rs b/src/tools/clippy/tests/workspace_test/subcrate/src/lib.rs index 8b137891791..8b137891791 100644 --- a/src/tools/clippy/clippy_workspace_tests/subcrate/src/lib.rs +++ b/src/tools/clippy/tests/workspace_test/subcrate/src/lib.rs diff --git a/src/tools/miri b/src/tools/miri -Subproject 824816c973a3fd0596ae3a9a38c6fb6299b913b +Subproject deb9bfd24648d50142ab29b810175837c471888 diff --git a/src/tools/rust-analyzer b/src/tools/rust-analyzer -Subproject 8e9ccbf97a70259b6c6576e8fd7d77d28238737 +Subproject 0f8c96c92689af8378dbe9f466c6bf15a3a2745 diff --git a/src/tools/rustfmt/Configurations.md b/src/tools/rustfmt/Configurations.md index a89fbe863e6..4476f2a449b 100644 --- a/src/tools/rustfmt/Configurations.md +++ b/src/tools/rustfmt/Configurations.md @@ -929,9 +929,9 @@ fn add_one(x: i32) -> i32 { ## `format_generated_files` Format generated files. A file is considered generated -if any of the first five lines contains `@generated` marker. +if any of the first five lines contain a `@generated` comment marker. -- **Default value**: `false` +- **Default value**: `true` - **Possible values**: `true`, `false` - **Stable**: No (tracking issue: [#5080](https://github.com/rust-lang/rustfmt/issues/5080)) diff --git a/src/tools/rustfmt/src/config/mod.rs b/src/tools/rustfmt/src/config/mod.rs index 5dbe532ac38..cd90e0904b6 100644 --- a/src/tools/rustfmt/src/config/mod.rs +++ b/src/tools/rustfmt/src/config/mod.rs @@ -138,7 +138,7 @@ create_config! { inline_attribute_width: usize, 0, false, "Write an item and its attribute on the same line \ if their combined width is below a threshold"; - format_generated_files: bool, false, false, "Format generated files"; + format_generated_files: bool, true, false, "Format generated files"; // Options that can change the source code beyond whitespace/blocks (somewhat linty things) merge_derives: bool, true, true, "Merge multiple `#[derive(...)]` into a single one"; @@ -606,7 +606,7 @@ blank_lines_lower_bound = 0 edition = "2015" version = "One" inline_attribute_width = 0 -format_generated_files = false +format_generated_files = true merge_derives = true use_try_shorthand = false use_field_init_shorthand = false diff --git a/src/tools/rustfmt/src/formatting.rs b/src/tools/rustfmt/src/formatting.rs index 67cf1232f66..ca93955a549 100644 --- a/src/tools/rustfmt/src/formatting.rs +++ b/src/tools/rustfmt/src/formatting.rs @@ -80,7 +80,9 @@ fn should_skip_module<T: FormatHandler>( return true; } - if !config.format_generated_files() { + // FIXME(calebcartwright) - we need to determine how we'll handle the + // `format_generated_files` option with stdin based input. + if !input_is_stdin && !config.format_generated_files() { let source_file = context.parse_session.span_to_file_contents(module.span); let src = source_file.src.as_ref().expect("SourceFile without src"); diff --git a/src/tools/rustfmt/src/lib.rs b/src/tools/rustfmt/src/lib.rs index ad23b16e02e..fae8080c02e 100644 --- a/src/tools/rustfmt/src/lib.rs +++ b/src/tools/rustfmt/src/lib.rs @@ -3,6 +3,7 @@ #![warn(unreachable_pub)] #![recursion_limit = "256"] #![allow(clippy::match_like_matches_macro)] +#![allow(unreachable_pub)] #[macro_use] extern crate derive_new; diff --git a/src/tools/rustfmt/src/test/mod.rs b/src/tools/rustfmt/src/test/mod.rs index cceb28dfea6..c50d18644b0 100644 --- a/src/tools/rustfmt/src/test/mod.rs +++ b/src/tools/rustfmt/src/test/mod.rs @@ -488,6 +488,24 @@ fn stdin_disable_all_formatting_test() { } #[test] +fn stdin_generated_files_issue_5172() { + init_log(); + let input = Input::Text("//@generated\nfn main() {}".to_owned()); + let mut config = Config::default(); + config.set().emit_mode(EmitMode::Stdout); + config.set().format_generated_files(false); + config.set().newline_style(NewlineStyle::Unix); + let mut buf: Vec<u8> = vec![]; + { + let mut session = Session::new(config, Some(&mut buf)); + session.format(input).unwrap(); + assert!(session.has_no_errors()); + } + // N.B. this should be changed once `format_generated_files` is supported with stdin + assert_eq!(buf, "stdin:\n\n//@generated\nfn main() {}\n".as_bytes()); +} + +#[test] fn format_lines_errors_are_reported() { init_log(); let long_identifier = String::from_utf8(vec![b'a'; 239]).unwrap(); diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 4c28655bc86..9d2aa0f21c8 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -73,6 +73,7 @@ const RESTRICTED_DEPENDENCY_CRATES: &[&str] = &["rustc_driver", "rustc_codegen_l const PERMITTED_DEPENDENCIES: &[&str] = &[ "addr2line", "adler", + "ahash", "aho-corasick", "annotate-snippets", "ansi_term", diff --git a/src/tools/tidy/src/edition.rs b/src/tools/tidy/src/edition.rs index f610dbd806a..b0abee45986 100644 --- a/src/tools/tidy/src/edition.rs +++ b/src/tools/tidy/src/edition.rs @@ -2,11 +2,6 @@ use std::path::Path; -fn is_edition_2018(mut line: &str) -> bool { - line = line.trim(); - line == "edition = \"2018\"" -} - fn is_edition_2021(mut line: &str) -> bool { line = line.trim(); line == "edition = \"2021\"" @@ -23,27 +18,13 @@ pub fn check(path: &Path, bad: &mut bool) { return; } - // Not all library crates are ready to migrate to 2021. - if file.components().any(|c| c.as_os_str() == "library") - && file.components().all(|c| c.as_os_str() != "std") - { - let has = contents.lines().any(is_edition_2018); - if !has { - tidy_error!( - bad, - "{} doesn't have `edition = \"2018\"` on a separate line", - file.display() - ); - } - } else { - let is_2021 = contents.lines().any(is_edition_2021); - if !is_2021 { - tidy_error!( - bad, - "{} doesn't have `edition = \"2021\"` on a separate line", - file.display() - ); - } + let is_2021 = contents.lines().any(is_edition_2021); + if !is_2021 { + tidy_error!( + bad, + "{} doesn't have `edition = \"2021\"` on a separate line", + file.display() + ); } }, ); diff --git a/src/version b/src/version index bb120e876c6..4d5fde5bd16 100644 --- a/src/version +++ b/src/version @@ -1 +1 @@ -1.59.0 +1.60.0 |
